画像解析をUnityでやってみよう。7回目です。
ちょっと間空いちゃいました。すみません。
前回は五線譜の読み取りを試しましたが、もう少し改善する必要がありそうでした。
そこで今回は、細線化処理を使用して、線分検出前の前処理を重点的に行います。

細線化を使う

細線化とは?

細線化処理とは、画像処理における前処理の一種で、文字解析などによく使われるそうです。
例えば、文字の中には太い線や細い線が混在しますが、解析する上では不要な情報です。
そのため、一旦すべて同じ太さにして形だけを見ましょうという感じです。(僕の理解の範疇です。)
細線化については、ここを参考にするとイメージが掴みやすいのではないでしょうか。
今回はこのロジックを用いて、五線譜の太い線をすべて均等な太さにして、線分検出を行ってみます。

OpenCVに細線化は用意されていない!

では早速、OpenCVで細線化処理といきたいところですが、OpenCVにはなぜかデフォルトで用意されていません。
そのため、自前で実装する必要があります。(よく使うらしいのになんでないんだ。。)
ただ、いろいろな方が細線化のロジックについて細かく書いてくれているので、調べればそこまで困る事はないでしょう。
このサイトが各アルゴリズムの比較などもやっていてとても参考になりました。

さて、どのアルゴリズムを使おうかな。。と悩んでいる時に、ふと、OpenCVforUnityで実装されてたりしないかな?と思い、Thinningでプロジェクト検索してみました。

あるやん。

やるじゃないか。OpenCVforUnity。OpenCVでは実装されていないらしいですが、OpenCVforUnityでは実装ありました。やったね。

OpenCVforUnityでは細線化処理が追加されているよ

OpenCVforUnityに、Ximgprocというクラスがあるので、その中に実装があります。
中でライブラリ読んでいるだけなので、OpenCVの拡張用のライブラリとかも使っているっぽいですね。
一応アルゴリズムは、デフォルトでZhangsuenになっているようです。あと選べるのは、GUOHALLてやつですね。
まあとりあえずZhangsuenのアルゴリズムはよく見かけるので、これ使ってやってみます。

細線化する前に

2値化して白黒反転する

細線化するためには、2値化していないといけません。
ロジックをみるとわかるのですが、白の領域に対して、周りの黒の領域を調べて、条件にマッチしていたら黒に置き換えていきます。
という事で、2値化するときには、BINARY_INVを指定して、白黒を反転します。

しかし、画像をみるとわかる通り、2値化の時点で線が飛んでしまう場合があります。

2値化


なぜかというと、影や光の反射などの影響があるからです。
普通に2値化をすると、各画素に対して閾値以上を白、以下を黒といった処理になるので、影や反射に極端に弱いのです。
写真に対する処理であれば閾値をいい感じに持っていけば済むと思いますが、動画であれば、そうはいきません。

適応的閾値処理で影や反射に強くする

そこで、適応的閾値処理を使います。
これを使用すると、各画素の周りから判断して2値化してくれます。
どこまでの周りをみるかは、BlockSizeというパラメータを調整する事になります。
これが小さすぎると、かなりのノイズが出てしまいますが、いい感じに調整できれば、綺麗に抜き取る事が可能です。

BlockSize=5 BlockSize=11
2値化 Canny変換

今回はBlockSizeが11くらいがちょうどよかったので、11にしました。
これは開発するものによって変わるので、調整が必要です。
という事で、これで影や光に対してわりかしフラットな2値化画像を取る事ができました。

細線化してみる

2値化もうまくできているので、今度は細線化をしてみます。 Canny変換 結構綺麗に線が出ています。
これなら線分検出をしても良さそうですね。

線分検出をしてみる

細線化した画像を元に、線分検出を行います。
さらに今回は、五線譜を検出するために線分をマージしました。
線分が綺麗に取れるように閾値だけ調整していい感じになればいいですが、ちょっと線が途切れたりというのは避けられないので、途切れている線分などを同一線分とみなすようにします。
結果はこんな感じ。

Canny変換 Canny変換

赤が検出した線分、緑がマージ後線分、黄色が五線譜です。 線分は大体いいですね。
五線譜の認識率ひくい。。
五線譜だという認識の条件が固すぎるっぽいです。
平行な等間隔の線分が5本という条件でやっているのですが、線分検出時に真ん中の線分がちょっと飛んじゃったりすることもあるので、
もっとゆるい条件がよさそうですね。ここら辺は機械学習など入れるともしかしてよくなっていくのかもしれません。
あとはすべてOpenCVの機能しかつかっていないので、何か自前でやらないと難しいとかもあるかもですね。
すでにわかっているのは、線分が水平である方が良いという事です。そのために範囲を抜き出して、変形する必要もあるかもしれません。
まあ大体読んでますって事で。。


という事で、今回は細線化をしてから五線譜の検出をやってみました。
五線譜の調整は後回しにして、次は音符を読んでみます。
画像処理において、前処理は超重要です。どれだけ欲しい情報以外を抜き取れるかにかかっている気がします。

紹介している以外にもブラーをかけたりとかいろいろな方法があるみたいなので、それぞれに合ったものを試してみてください。

それではまた次回。さようなり〜