Symfoware

Symfowareについての考察blog

OpenCV + Pythonで特定の図形を検出する2(特徴量検出による図形の判定)

OpenCVで図形の部分のみ判定することができました。
OpenCV + Pythonで特定の図形を検出する1(図形の領域を矩形で取得)

各図形ごとに特徴量を検出。
図形の判定が行えるか試してみます。


画像のくり抜きと特徴量検出



矩形で囲った図形をくり抜き。
特徴量判定した結果を描画してみます。


  1. # -*- coding:utf-8 -*-
  2. import cv2 as cv
  3. def main():
  4.     # ファイルを読み込み
  5.     image_file = 'target.png'
  6.     src = cv.imread(image_file, cv.IMREAD_COLOR)
  7.     # 画像の大きさ取得
  8.     height, width, channels = src.shape
  9.     image_size = height * width
  10.     # グレースケール化
  11.     img_gray = cv.cvtColor(src, cv.COLOR_RGB2GRAY)
  12.     # しきい値指定によるフィルタリング
  13.     retval, dst = cv.threshold(img_gray, 127, 255, cv.THRESH_TOZERO_INV )
  14.     # 白黒の反転
  15.     dst = cv.bitwise_not(dst)
  16.     # 再度フィルタリング
  17.     retval, dst = cv.threshold(dst, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
  18.     # 輪郭を抽出
  19.     dst, contours, hierarchy = cv.findContours(dst, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
  20.     for i, contour in enumerate(contours):
  21.         # 小さな領域の場合は間引く
  22.         area = cv.contourArea(contour)
  23.         if area < 500:
  24.             continue
  25.         # 画像全体を占める領域は除外する
  26.         if image_size * 0.99 < area:
  27.             continue
  28.         # 外接矩形を取得
  29.         x,y,w,h = cv.boundingRect(contour)
  30.         # 外接矩形で囲まれた箇所のイメージを切り出し
  31.         cut = dst[y:y+h, x:x+w]
  32.         
  33.         # 特徴量検出機を作成し解析
  34.         detector = cv.FastFeatureDetector_create()
  35.         detector.setNonmaxSuppression(False)
  36.         keypoints = detector.detect(cut)
  37.         # 画像への特徴点の書き込み
  38.         cut = cv.drawKeypoints(cut, keypoints, None)
  39.         cv.imwrite('debug_%d.png' % i, cut)
  40.         
  41.     
  42. if __name__ == '__main__':
  43.     main()



得られた画像です。

869_01.png

869_02.png

869_03.png

869_04.png

ここで気が付きましたが、四角や三角は特徴的な点があまり存在せず検出が難しそうです。
「矢印」と「星」の図形の判定に注力してみます。



特徴量の比較



比較元として、こんな画像を切り出しました。

・arrow.png
869_05.png

・star.png
869_06.png

上記画像から得られた特徴点との比較を行い、画像の種類判定を行ってみます。
まずは切り抜き元の画像から比較。
これは100%一致となるはずです。

なお、特徴量の比較は過去にやったことがあったのでこれを参考にしました。
OpenCV を使用して、写真に特定の人物が写っているか判定する
OpenCV 写真に特定の人物が写っているか判定 その2

試した当時はOpenCV 2系です。
OpenCV 3系で随分メソッド名が変更になっているようで、こちらを参考に読み替えました。

OpenCV3とPython3で特徴点を抽出する(AgastFeature, FAST, GFTT, MSER, AKAZE, BRISK, KAZE, ORB, SimpleBlob, SIFT)
PythonとOpenCVを用いた顔の類似度判定についての話

とりあえず動くソースです。


  1. import cv2 as cv
  2. def main():
  3.     # ファイルを読み込み
  4.     image_file = 'target.png'
  5.     src = cv.imread(image_file, cv.IMREAD_COLOR)
  6.     # 画像の大きさ取得
  7.     height, width, channels = src.shape
  8.     image_size = height * width
  9.     # グレースケール化
  10.     img_gray = cv.cvtColor(src, cv.COLOR_RGB2GRAY)
  11.     # しきい値指定によるフィルタリング
  12.     retval, dst = cv.threshold(img_gray, 127, 255, cv.THRESH_TOZERO_INV )
  13.     # 白黒の反転
  14.     dst = cv.bitwise_not(dst)
  15.     # 再度フィルタリング
  16.     retval, dst = cv.threshold(dst, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
  17.     # 輪郭を抽出
  18.     dst, contours, hierarchy = cv.findContours(dst, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
  19.     # 特徴量検出機を作成
  20.     detector = cv.ORB_create(edgeThreshold=8, scoreType=cv.ORB_FAST_SCORE)
  21.     matcher = cv.BFMatcher(cv.NORM_HAMMING)
  22.     _, arrow = detector.detectAndCompute(cv.imread('arrow.png'), None)
  23.     _, star = detector.detectAndCompute(cv.imread('star.png'), None)
  24.     for i, contour in enumerate(contours):
  25.         # 小さな領域の場合は間引く
  26.         area = cv.contourArea(contour)
  27.         if area < 500:
  28.             continue
  29.         # 画像全体を占める領域は除外する
  30.         if image_size * 0.99 < area:
  31.             continue
  32.         # 外接矩形を取得
  33.         x,y,w,h = cv.boundingRect(contour)
  34.         # 外接矩形で囲まれた箇所のイメージを切り出し
  35.         cut = dst[y:y+h, x:x+w]
  36.         _, des_orb = detector.detectAndCompute(cut, None)
  37.         
  38.         # 特徴量検出機を作成し解析
  39.         matches = matcher.match(arrow, des_orb)
  40.         dist = [m.distance for m in matches]
  41.         # 差の平均
  42.         ret = sum(dist) / len(dist)
  43.         # 少ないほど一致
  44.         if ret < 10:
  45.             print(ret, 'arrow')
  46.         
  47.         matches = matcher.match(star, des_orb)
  48.         dist = [m.distance for m in matches]
  49.         # 差の平均
  50.         ret = sum(dist) / len(dist)
  51.         # 少ないほど一致
  52.         if ret < 10:
  53.             print(ret, 'star')
  54.         
  55.     
  56. if __name__ == '__main__':
  57.     main()



比較元の画像を切り出した元画像を解析してみると、最大スコア0で矢印と星がみつかりました。


$ python3 sample.py
[ INFO:0] Initialize OpenCL runtime...
0.0 arrow
0.0 star





実際の解析



別の画像を用意してテストしてみます。

869_07.png

869_08.png

パラメーターを微調整したソースがこちら。


  1. import cv2 as cv
  2. def main():
  3.     # ファイルを読み込み
  4.     image_file = 'target.png'
  5.     src = cv.imread(image_file, cv.IMREAD_COLOR)
  6.     # 画像の大きさ取得
  7.     height, width, channels = src.shape
  8.     image_size = height * width
  9.     # グレースケール化
  10.     img_gray = cv.cvtColor(src, cv.COLOR_RGB2GRAY)
  11.     # しきい値指定によるフィルタリング
  12.     retval, dst = cv.threshold(img_gray, 127, 255, cv.THRESH_TOZERO_INV )
  13.     # 白黒の反転
  14.     dst = cv.bitwise_not(dst)
  15.     # 再度フィルタリング
  16.     retval, dst = cv.threshold(dst, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
  17.     # 輪郭を抽出
  18.     dst, contours, hierarchy = cv.findContours(dst, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
  19.     # 特徴量検出機を作成
  20.     detector = cv.ORB_create(edgeThreshold=6, scoreType=cv.ORB_FAST_SCORE)
  21.     matcher = cv.BFMatcher(cv.NORM_HAMMING)
  22.     _, arrow = detector.detectAndCompute(cv.imread('arrow.png'), None)
  23.     _, star = detector.detectAndCompute(cv.imread('star.png'), None)
  24.     for i, contour in enumerate(contours):
  25.         # 小さな領域の場合は間引く
  26.         area = cv.contourArea(contour)
  27.         if area < 500:
  28.             continue
  29.         # 画像全体を占める領域は除外する
  30.         if image_size * 0.99 < area:
  31.             continue
  32.         # 外接矩形を取得
  33.         x,y,w,h = cv.boundingRect(contour)
  34.         # 外接矩形で囲まれた箇所のイメージを切り出し
  35.         cut = dst[y:y+h, x:x+w]
  36.         _, des_orb = detector.detectAndCompute(cut, None)
  37.         
  38.         # 特徴量検出機を作成し解析
  39.         matches = matcher.match(arrow, des_orb)
  40.         dist = [m.distance for m in matches]
  41.         # 差の平均
  42.         ret = sum(dist) / len(dist)
  43.         # 少ないほど一致
  44.         if ret < 40:
  45.             print(ret, 'arrow')
  46.             cv.imwrite('debug_%d.png' % i , cut)
  47.         
  48.         matches = matcher.match(star, des_orb)
  49.         dist = [m.distance for m in matches]
  50.         # 差の平均
  51.         ret = sum(dist) / len(dist)
  52.         # 少ないほど一致
  53.         if ret < 40:
  54.             print(ret, 'star')
  55.             cv.imwrite('debug_%d.png' % i , cut)
  56.         
  57.     
  58. if __name__ == '__main__':
  59.     main()



最初の画像でテスト


$ python3 sample.py
[ INFO:0] Initialize OpenCL runtime...
33.291666666666664 arrow



二番目の画像


$ python3 sample.py
[ INFO:0] Initialize OpenCL runtime...
29.705882352941178 star




検出できる特徴点が少ないからか、あまり制度が上がらないですね。



回転



傾けたり回転した画像でも試してみます。

869_09.png

869_10.png

869_11.png

・1番目
検出できず

2番目
検出できず

3番目
星を検出


$ python3 sample.py
[ INFO:0] Initialize OpenCL runtime...
31.784313725490197 star



869_12.png

まだまだ改善の余地ありです。



【参考URL】

OpenCV3とPython3で特徴点を抽出する(AgastFeature, FAST, GFTT, MSER, AKAZE, BRISK, KAZE, ORB, SimpleBlob, SIFT)
PythonとOpenCVを用いた顔の類似度判定についての話

関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2018/04/30(月) 23:23:27|
  2. Python
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
次のページ