10
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

音楽ど素人が機械学習で楽譜認識するAndroidアプリを作る話 五線譜の検出編

Last updated at Posted at 2019-06-21

#はじめに
中学から吹奏楽部に入った息子が楽譜に音名を書くのがだるいと言いだしたんで、そんなもん今どきスマホとパシャッとすりゃ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

五線譜検出のプロセス

楽譜認識アプリということで音符はまあ機械学習の認識モデル作成をがんばるとして、五線譜は単なる線なんだから、線分さえ検出できればいけるだろと簡単に考えてました
がしかし、やってみたらこれが結局のところ音符の認識より大変だったかもしれない
最終的なプロセスは以下の通り

  1. 画像の平滑化
  2. 画像の2値化
  3. 線分の検出
  4. 五線譜の検出

開発環境

画像処理はOpenCV for Androidを使って処理していますので、プロジェクトに入れておく必要があります
OpenCVのAndroid Studioへのインポート方法は以下を参考
https://qiita.com/kodai100/items/6c9e8a34d0714913c017

画像の平滑化

五線譜検出の下準備として画像の平滑化を行います
これをしないと、のちに行う2値化のときに結構ノイズが出てしまいます

detect_score.java
Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGB2GRAY);
Photo.fastNlMeansDenoising(mat, den, 6, 7, 21);

img_adp3g.png

OpenCVでの画像の平滑化はblurやmedianBlurもありますが、処理時間はかかるけど一番ノイズがきれいに消せたPhoto.fastNlMeansDenoisingを採用

画像の2値化

OpenCVによる画像の2値化にはいろいろ種類があります
しきい値処理による2値化
img_bin2.png
大津の2値化
img_otu2.png
適応的しきい値処理による2値化
img_adp2.png
今回はスマホで楽譜の写真を撮る前提なので撮影時に影ができやすかったりすることから、陰影の影響を回避できる適応的しきい値処理を採用しました

detect_score.java
Imgproc.adaptiveThreshold(den, adp, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 17, 2);

線分の検出

音名を表記するためにはまず五線譜をということで、線分を検出するためにOpenCVのハフ変換による直線検出は白い部分に対して線分を捜索するんで、まずは2値化画像を反転して検出を実行

detect_score.java
Core.bitwise_not(adp, bin);
Mat linesMat = new Mat();
Imgproc.HoughLinesP(mat, linesMat, 1d, Math.PI / 360d, width / 80, width / 80d, 7d);

パラメータはとにかく五線譜の線分をできるだけとれるように調整
img_line2.png

五線譜の検出

検出された線分から五線譜を見つけるわけですが、最初はとにかく長いのを抜き出せばそれが五線譜だろうと思ってやってみました
結果長く見える線分は実は細切れだったりして、それならばとつながってそうな線をつなげて判断してみるかってやってみても、これがまあうまくいかない
img_line3.png
検出した線分をランダムに色を変えて描画、長い線分に見えて実は細切れになっている

そこで長い線分を見つけるって考えは捨てて、上から下に垂線を引いて全部の線分との交点を出し、その交点が縦に均等に5点並んでいるものを検出してみた
img_grd2.png
そこから見つけた5点の幅の最大値や最小値を決めて抽出すると、うまい具合に五線譜っぽいところを拾えたので、こっからさらに横にきっちり並んでるかを評価して正しく五線譜を検出することができた

10
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?