今回はSIFTを扱います。
SIFT (Scale-Invariant Feature Transform)の導入 — OpenCV-Python Tutorials 1 documentation
SIFTは、2004年にブリティッシュコロンビア大学のD.Loweが発表したもので、 キーポイントの検出、特徴量の計算を行うアルゴリズムとのことです。
おおざっぱに自分なりにまとめると、
- ステレオ撮影画像や動画での物体追跡では、複数画像間で対応する点を見つける必要があるが、 これに使える画像上の点(特徴点)を見つけ、また、その点の特徴ベクトルを生成するアルゴリズム
- Harrisのコーナー検出でも特徴点検出はできるが、スケール不変でない(拡大・縮小されたり、 撮影時の距離が違う、撮影したカメラの解像度が違う、等に影響される)
- SIFTでは複数の空間スケールで特徴点検出を行うので、スケール不変性を持つ
- 回転不変性も持たせるため、特徴点近傍の角度ごとのヒストグラムを計算して、 回転角を計算する
- 特徴点の特徴量としては、特徴点周囲の4x4ブロック16個のヒストグラムを使う、 各々ビンの数8で、結果1特徴点あたり128要素のベクトルを持つ
以下も参考になりました。
https://qiita.com/icoxfog417/items/adbbf445d357c924b8fc
特許が取られていたようですが、2020年3月に特許権終了になったようです。
OpenCVでフリーに使えます。
SIFTの特許が切れてOpenCV v4.4.0から普通に使えるようになってた話 - Qiita
実践
やはり前回と同じ画像を使います。
まずは1つめの画像で様子見。
sift = cv2.SIFT_create() kp1 = sift.detect(img1_gray, None) img1_kp = cv2.drawKeypoints(img1, kp1, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) cv2.imshow('KeyPoints1', img1_kp)
こんな形の特徴点が検出されます。いまいち規則性が分かりませんが…
sift.detect()
での計算結果を少し確認。
>>> type(kp1) <class 'list'> >>> len(kp1) 1334 >>> type(kp1[0]) <class 'cv2.KeyPoint'> >>> kp1[0].pt (25.444393157958984, 6.472270965576172) >>> kp1[0].size 2.4101293087005615 >>> kp1[0].angle 169.22996520996094 >>> kp1[0].response 0.015413731336593628 >>> kp1[0].octave 4588287 >>> kp1[0].class_id -1
cv2.KeyPoint
型のリストで、座標、サイズ(キーポイントの直径)、角度、キーポイントの強さ、
といった情報を含みます。
OpenCV: cv::KeyPoint Class Reference
以下気づき点。
- チュートリアルでは
cv2.SIFT()
を使っていましたが、それでやるとなぜかその後のsift.detect()
を実行したときにpythonが落ちてしまった…cv2.SIFT_create()
なら大丈夫でした。 cv2.drawKeypoints()
で特徴点検出しますが、第3引数が必要でした。color
という引数ですが、いまいち何をする引数なのか分からず…
参考
OpenCV: Drawing Function of Keypoints and Matches
color
を変更した結果
img1_kp = cv2.drawKeypoints(img1, kp1, (255,0,0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) cv2.imshow('KeyPoints1', img1_kp)
描画の色は変わってるけども…
残りの2画像でもやってみます。
同じシール台紙を違う角度から撮っているので、同じような特徴点が出ることを期待。
kp2 = sift.detect(img2_gray, None) img2_kp = cv2.drawKeypoints(img2, kp2, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) kp3 = sift.detect(img3_gray, None) img3_kp = cv2.drawKeypoints(img3, kp3, None, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) imgs_kp = np.hstack((img1_kp, img2_kp, img3_kp)) cv2.imshow('KeyPoints', imgs_kp)
こっちのほうならまだ規則性が見えます。
- シールとシールの隙間に特徴点があることが多い
特徴点が検出されていない箇所もあります。 - 右の2つの画像でシール台紙上の同じ点に特徴点が現れていることもある
一応cv2.detect()
のflags
を変えてみました。
img1_kp = cv2.drawKeypoints(img1, kp1, None, flags=cv2.DRAW_MATCHES_FLAGS_DEFAULT) img2_kp = cv2.drawKeypoints(img2, kp2, None, flags=cv2.DRAW_MATCHES_FLAGS_DEFAULT) img3_kp = cv2.drawKeypoints(img3, kp3, None, flags=cv2.DRAW_MATCHES_FLAGS_DEFAULT) imgs_kp = np.hstack((img1_kp, img2_kp, img3_kp)) cv2.imshow('KeyPoints', imgs_kp)
DEFAULT設定だと、大きさや方向の表現がなくなるようです。
分かりにくくなったし、あんまり面白くない…
特徴量
sift.compute()
で特徴量が計算できます。
>>> kp1, des1 = sift.compute(img1_gray, kp1) >>> type(des1) <class 'numpy.ndarray'> >>> des1.shape (1334, 128) >>> des1[0] array([ 0., 1., 0., 4., 108., 25., 1., 0., 62., 17., 0., 3., 146., 11., 0., 0., 146., 49., 1., 4., 18., 5., 1., 4., 8., 5., 1., 6., 36., 10., 1., 2., 5., 6., 1., 12., 146., 12., 0., 0., 71., 11., 0., 9., 146., 12., 0., 1., 146., 21., 0., 1., 31., 10., 2., 15., 20., 1., 1., 2., 63., 17., 1., 7., 17., 2., 0., 1., 138., 42., 1., 4., 58., 1., 0., 0., 146., 64., 2., 8., 146., 10., 0., 0., 40., 15., 2., 17., 35., 5., 1., 4., 87., 19., 1., 3., 7., 0., 0., 3., 68., 9., 0., 2., 27., 0., 0., 3., 85., 19., 1., 7., 146., 1., 0., 0., 21., 12., 2., 26., 21., 1., 0., 0., 60., 37., 3., 4.], dtype=float32)
確かに1点あたり128次元のベクトルとなっています。
以上
まだ有用性は見えませんが、後々の特徴点マッチングで使えるかと思います。
次はSURFでの特徴点検出です。