#はじめに
中学から吹奏楽部に入った息子が楽譜に音名を書くのがだるいと言いだしたんで、そんなもん今どきスマホとパシャッとすりゃAIでぱっと出てくるアプリがあるだろ?って言ったら意外となかったんで自分で作った
ちなみにiOS用だといくつかあります
スマホ用の楽譜認識アプリをまとめてみた
http://anadreline.blogspot.com/2019/06/wo.html
#作成アプリ
Music Score Reader
https://play.google.com/store/apps/details?id=msreader.android.anadreline.com.musicscorereader
#開発者の初期スペック
- 五線譜のどこがドなのかがわからない
- **♯と♭って結局どっちかだけでよくない!?**と思っている
- 息子にトランペットのドはドじゃないからねって言われてファッ!?ってなる
- 12音に五線譜表記って仕様がおかしくねって思っている
#検証環境
この記事の内容は、以下の環境で検証しました。
- Android Studio 3.2.1
- CompileSdkVersion:27
- MinSdkVersion:21
- TargetSdkVersion:27
- OpenCV for Android 3.2.3
- TensorFlow Mobile
五線譜検出のプロセス
楽譜認識アプリということで音符はまあ機械学習の認識モデル作成をがんばるとして、五線譜は単なる線なんだから、線分さえ検出できればいけるだろと簡単に考えてました
がしかし、やってみたらこれが結局のところ音符の認識より大変だったかもしれない
最終的なプロセスは以下の通り
- 画像の平滑化
- 画像の2値化
- 線分の検出
- 五線譜の検出
開発環境
画像処理はOpenCV for Androidを使って処理していますので、プロジェクトに入れておく必要があります
OpenCVのAndroid Studioへのインポート方法は以下を参考
https://qiita.com/kodai100/items/6c9e8a34d0714913c017
画像の平滑化
五線譜検出の下準備として画像の平滑化を行います
これをしないと、のちに行う2値化のときに結構ノイズが出てしまいます
Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGB2GRAY);
Photo.fastNlMeansDenoising(mat, den, 6, 7, 21);
OpenCVでの画像の平滑化はblurやmedianBlurもありますが、処理時間はかかるけど一番ノイズがきれいに消せたPhoto.fastNlMeansDenoisingを採用
画像の2値化
OpenCVによる画像の2値化にはいろいろ種類があります
しきい値処理による2値化
大津の2値化
適応的しきい値処理による2値化
今回はスマホで楽譜の写真を撮る前提なので撮影時に影ができやすかったりすることから、陰影の影響を回避できる適応的しきい値処理を採用しました
Imgproc.adaptiveThreshold(den, adp, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 17, 2);
線分の検出
音名を表記するためにはまず五線譜をということで、線分を検出するためにOpenCVのハフ変換による直線検出は白い部分に対して線分を捜索するんで、まずは2値化画像を反転して検出を実行
Core.bitwise_not(adp, bin);
Mat linesMat = new Mat();
Imgproc.HoughLinesP(mat, linesMat, 1d, Math.PI / 360d, width / 80, width / 80d, 7d);
パラメータはとにかく五線譜の線分をできるだけとれるように調整
五線譜の検出
検出された線分から五線譜を見つけるわけですが、最初はとにかく長いのを抜き出せばそれが五線譜だろうと思ってやってみました
結果長く見える線分は実は細切れだったりして、それならばとつながってそうな線をつなげて判断してみるかってやってみても、これがまあうまくいかない
検出した線分をランダムに色を変えて描画、長い線分に見えて実は細切れになっている
そこで長い線分を見つけるって考えは捨てて、上から下に垂線を引いて全部の線分との交点を出し、その交点が縦に均等に5点並んでいるものを検出してみた
そこから見つけた5点の幅の最大値や最小値を決めて抽出すると、うまい具合に五線譜っぽいところを拾えたので、こっからさらに横にきっちり並んでるかを評価して正しく五線譜を検出することができた