先日のHarrisコーナー検出をその後、試してみましたが、どうも検出精度がイマイチ。
どうやら Harrisコーナー検出は拡大縮小のスケール変化に弱いらしい。
テイラー/マクローリン展開からのHarrisコーナー検出 (その2) - end0tknr's kipple - 新web写経開発
そこで、今回はSIFT特徴点検出を写経(Scale-Invariant Feature Transform).
SIFT特徴点の定義等については、別の機会で
SIFT特徴点検出とは?
http://www.vision.cs.chubu.ac.jp/sift/
上記urlに丁寧が解説がありますが、私の理解は次の通り。
SIFTでは、ガウシアン差分(DoG)を用いて抽出します。
- Iσは、Gσによってぼかした画像
- kは、スケールの分離度を決める定数
- *は、畳み込みの意味
特徴点は、座標とスケールσの空間におけるD(x,σ)の極大点、極小点。
以上で特徴点の座標とスケールを得ることができ、次に特徴点の記述を行います。
SIFTでは回転に対し不変にする為、周辺の画像勾配と向きに基き、 主要な参照方向を決定します。 先日のHarrisでは正規化相互相関を用いましたが、 SIFTでは明度に対しロバストにする為、画像勾配を用います。
前準備 - install vlfeat
SIFT特徴量検出自体は、外部ライブラリに任せます
cd ~/local wget http://www.vlfeat.org/download/vlfeat-0.9.20.tar.gz tar -xvf vlfeat-0.9.20.tar.gz cd vlfeat-0.9.20 make cd .. ln -s vlfeat-0.9.20/bin/glnxa64 vlfeat
vlfeat を使った python script
#!/usr/local/bin/python # -*- coding: utf-8 -*- from PIL import Image import numpy import matplotlib matplotlib.use('Agg') import matplotlib.pylab as plb import os IMNAME = 'empire.jpg' SIFT_CMD = '/home/endo/local/vlfeat/sift' PROCESS_IMAGE_PARAMS = "--edge-thresh 10 --peak-thresh 5" def main(): im1 = numpy.array(Image.open(IMNAME).convert('L')) # SIFT特徴点検出し、保存 process_image(IMNAME,'empire.sift') # SIFT特徴点を読み出し l1,d1 = read_features_from_file('empire.sift') # SIFT特徴点を描画 plb.figure() plb.gray() plot_features(im1,l1,circle=True) plb.savefig( '2_3_3.png' ) # 画像の特徴点を検出しfileに保存 def process_image(imagename,resultname,params=PROCESS_IMAGE_PARAMS): if imagename[-3:] != 'pgm': # pgmファイルを作成する im = Image.open(imagename).convert('L') imagename = 'tmp.pgm' im.save(imagename) cmmd = str(SIFT_CMD +" "+imagename+" --output="+resultname+ " "+params) os.system(cmmd) print 'processed to', resultname # SIFT特徴量を読み込んで行列形式で返す def read_features_from_file(filename): f = numpy.loadtxt(filename) return f[:,:4],f[:,4:] # 特徴点の配置と記述子 # 画像を特徴量とともに描画. # 入力:im(配列形式の画像)、locs(各特徴量の座標とスケール、方向) def plot_features(im,locs,circle=False): plb.imshow(im) if circle: for p in locs: draw_circle(p[:2],p[2]) else: plb.plot(locs[:,0],locs[:,1],'ob') plb.axis('off') def draw_circle(c,r): t = numpy.arange(0,1.01,.01)*2* numpy.pi x = r * numpy.cos(t) + c[0] y = r * numpy.sin(t) + c[1] plb.plot(x,y,'b',linewidth=2) if __name__ == '__main__': main()
↑こう書くと↓こう表示されます