OpenCVを用いたPythonで五線譜画像の認識を行いたいです.
そのため,同じ間隔で,同一の方向を向いている直線の検出を試みています.
直線検出はHough変換を用いればよいかと思いますが,等間隔の5本の線を検出するのにどうすればよいか悩んでいます.
皆様の知恵をお借りできれば幸いです.
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
内容は,2018/11/13 10:48のコメントの内容と同一です.
(この内容に関してのやりとりが発生したため,回答として分けました)
見つけたい線群が画像内で「水平な線」であることを仮定して良いならば,
直線の方向は既知ですから,直線の位置(y座標)だけを見つければよいわけです.
(ハフ変換として見れば,投票空間は1次元でよい.)
輝度勾配方向が画像上で上下方向になるエッジ点群だけのy座標のヒストグラムを作れば,5つずつの山ができるでしょうから,そこから線群のy座標を推定することができそうです.
(画像の具合次第ですが,「水平」を仮定できない場合でも,見つけたい線群の画像上での方向を,輝度勾配の主たる方向等から推定できるかもしれません.→方向が推定できれば以降の処理は同様.)
ヒストグラムの作成方法がわかりません.
度数分布を求めるだけですから,難しい話ではありません。
画像のy方向サイズ分の配列でも用意して(要素は全0に初期化しておき),画像の全画素を走査して,エッジ点のy座標に対応する配列要素をインクリメントすることを繰り返せばよいでしょう.
とりあえず,ものすごーく雑に実装してみました.
(当方,pythonがダメなので,C++ですが)
int main(void) { //画像読込 cv::Mat Src = cv::imread( "SrcImg.png", CV_LOAD_IMAGE_GRAYSCALE ); if( Src.empty() )return 0; cv::imshow( "Src", Src ); //ヒストグラム作成 std::vector< unsigned int > Hist( Src.rows, 0 ); //y方向サイズ分の配列.全要素0で初期化. for( int y=1; y+1<Src.rows; ++y ) { const unsigned char *pU = Src.ptr<unsigned char>( y-1 ); const unsigned char *pC = Src.ptr<unsigned char>( y ); const unsigned char *pD = Src.ptr<unsigned char>( y+1 ); for( int x=1; x+1<Src.cols; ++x ) { int Dx = std::max( abs( pC[x]-pC[x-1] ), abs( pC[x]-pC[x+1] ) ); int Dy = std::max( abs( pC[x]-pU[x] ), abs( pC[x]-pD[x] ) ); if( Dy>32 && Dy > Dx*4 ) //※エッジ点判定.雑すぎるが. { ++Hist[y]; } } } unsigned int MaxVal = *std::max_element( Hist.begin(), Hist.end() ); //ヒストグラムの最大値 {//表示:ヒストグラム画像 cv::Mat HistImg = cv::Mat::zeros( Src.rows, 100, CV_8U ); for( int y=0; y<Src.rows; ++y ){ cv::line( HistImg, cv::Point(0,y), cv::Point(100,y), cv::Scalar( 255.0 * Hist[y] / MaxVal ) ); } cv::imshow( "Hist", HistImg ); } //てきとー処理でヒストグラムの山(=線のy座標)を見つける std::vector< int > LineY; //ここに見つけた山のy座標群を格納 { unsigned int Thresh = MaxVal * 0.75; //閾値 int RiseY = -1; //閾値を越えた箇所記憶用.越えてないときは-1 for( int y=0; y<Hist.size(); ++y ) { if( RiseY < 0 ) { if( Hist[y]>=Thresh ) { RiseY = y; } } else { if( Hist[y]<Thresh ) { LineY.push_back( (RiseY + y)/2 ); RiseY = -1; } } } } {//表示:線検出結果 cv::Mat Result; cv::cvtColor( Src, Result, CV_GRAY2BGR ); for( int y : LineY ){ cv::line( Result, cv::Point(0,y), cv::Point(Result.cols,y), cv::Scalar( 0,0,255 ) ); } cv::imshow( "Lines", Result ); } // cv::waitKey(); return 0; }
結果の絵.
左から,原画,ヒストグラムを可視化したもの,線の位置を赤色で書いたもの.
(原画はてきとーにペイントで手書きしました.
線だけだと微秒なので,雑音として,小学校のときにこんな記号習ったような…?というのを何となく散りばめました.)
投稿2018/11/13 08:46
編集2018/11/13 10:43総合スコア11987
0
ベストアンサー
五線譜というものを知らないのですが、調べてでてきたこのような画像という認識でよろしいでしょうか?
Hough 変換で直線検出したあと、水平方向以外の線を検出対象外の直線として除去します。
直線は(始点、終点)という形で得られるので、楽譜の直線は水平方向に平行に並んでいるという仮定のもの、例えば、各直線の y 座標に注目し、以下のリストが得られたとします。
[直線1のy座標、直線2のy座標、...、直線nのy座標]
あとは楽譜も横棒がすべて5本ずつとか決まっているのであれば、
リストの最初の5本は1行目の楽譜の直線、6 ~ 10本目は2行目の楽譜の直線というようにすればよいのではないでしょうか?
投稿2018/11/12 15:32
総合スコア21956
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/11/13 01:48
2018/11/13 08:14
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/11/13 09:52
2018/11/13 10:47
2018/11/14 06:44