Ruby


Moravec作用素

前回は画像をグレースケールに変換しました。 今回はそれを利用して、Moravecオペレータ(Moravec作用素)を実装します。

理論

今回扱うのはNagelのMoravecオペレータです。この方法は簡潔に言うと、 濃度をz 軸方向の高さとするxyz 空間内の面を考え、輝度変化局面の曲率に基づいて特徴点を求める という特徴点抽出方法です。 たとえば、3×3 の探索範囲を考えた場合、輝度変化の大きさとして次のM(x,y)を画素ごとに計算し、 この値が大きい点を特徴点とします。 ただし、I(x,y)はピクセル(x,y)の輝度です。

コード

001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059

#! ruby -Ks # 2009/06/09 Shirao # moravec(nagel)作用素 lim=200000 #ここにいい閾値を指定しよう max=[]; point=0; #最大値、抽出ポイント数 co="" r=open(ARGV[0],"rb"){|io| #読むファイルを指定する co=io.read #全部読む co=co[15..-1] #ヘッダを飛ばす 本来ヘッダ長を自動的に取得すべき } gs=Array.new W=600 H=400 open("b.ppm","wb"){|io| io.printf("P6\n#{W} #{H}\n255\n") #ヘッダを書き込む (W*H).times{|pic| #サイズはW*H r=co[pic*3]; g=co[pic*3+1]; b=co[pic*3+2] #rgbの順番 gs[pic]=( (r*2+g*4+b) / 7 ).to_i #グレースケール変換式 } l=2 #探査域 5*5 pic=0 #(x, y) -> gs[x+W*y] #フィルタブロック W.times{|x| H.times{|y| pic=x+W*y nagel=0 for nx in x-l..x+l if (nx>=0)&&(nx<W)&&(nx!=x) for ny in y-l..y+l if (ny>=0)&&(ny<H)&&(ny!=y) np=nx+W*ny #特徴量 ただし除算を省略 nagel+=((gs[np]-gs[pic])**2) end end end end #点描画部(1*1の青色点で特徴点を示す) if(nagel>lim) point+=1 1.times{|i| 1.times{|j| trp=(pic-W-1)+i+W*j co[trp*3]=0; co[trp*3+1]=0; co[trp*3+2]=255; } } end max.push(nagel) #最大値算出配列 } puts x if(x%50)==0 #経過報告 } io.write co #値決めのヒントです print "Maximum score : ", max.sort[-1], "\n" print "100th score : ", max.sort[-100], "\n" print "Extracted point : ", point }

ポイント

境界値、探査域

このオペレータの性能を決定するのは境界値をどこに定めるか、 例では5*5で行いましたが探査域はどう定めるかということです。 どれぐらいの値をとるのかサンプルを最後に表示するようにしておきました。 ヒントになるかもしれません。

注意点と展望

コードを見てみると分かりますが、愚直にインプリメントしているため、 計算量が爆発してしまってます。このスピードでは実用的ではないです。 うまい方法を考えてみてください。
今回はnagelのmoravecオペレータを作ってみましたが、もっと別のものも探してみてください。 たとえばMoravecのInterestオペレータがこれの別バージョンです。 また、HarrisやTomasi-Kanadeも有名どころでしょう。 OpenCVを使うと簡単に実現できますので試してみても面白いかもしれませんよ。

付録

実行例

実際に適応してみました。入力と出力を以下に示します。 この例では特徴点を309個抽出したようです。ちょっと多いので、閾値をもっと大きくすべきでしょう。



ソースコード

Rubyソースコードを置いておきます。


プログラムに戻る