勉強しないとな~blog

ちゃんと勉強せねば…な電気設計エンジニアです。

OpenCVやってみる-14. SIFTによる特徴点検出

今回は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

OpenCVの商用利用 - ari23の研究ノート

実践

やはり前回と同じ画像を使います。

f:id:nokixa:20210706090007p:plain

まずは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)

f:id:nokixa:20210712225235p:plain

こんな形の特徴点が検出されます。いまいち規則性が分かりませんが…
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という引数ですが、いまいち何をする引数なのか分からず…

参考

画像の特徴点を抽出する - Qiita

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)

f:id:nokixa:20210713092305p:plain

描画の色は変わってるけども…

残りの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)

f:id:nokixa:20210713224656p:plain

こっちのほうならまだ規則性が見えます。

  • シールとシールの隙間に特徴点があることが多い
    特徴点が検出されていない箇所もあります。
  • 右の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)

f:id:nokixa:20210713225333p:plain

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での特徴点検出です。