Pythonで類似度検出③ 特徴点マッチング
久しぶりの更新です。
最近仕事が忙しいからかなかなか時間が取れません。
余談ですが、自分は月に一冊ぐらいのペースで技術書を買って読むのですが、「達人プログラマー」
という本が非常に面白かったです。プログラマなら読んで損はない本でした。
- 作者: Andrew Hunt,David Thomas,村上雅章
- 出版社/メーカー: オーム社
- 発売日: 2016/10/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (8件) を見る
今回は特徴点を使って類似度を検出したいと思います。
画像ファイル
今回、テストに使うのはこちらの画像です。
アインシュタイン einstein.jpg
女性 girl.jpg
男性 man.jpg
紳士 man2.jpg
おばあさん old_woman.jpg
アインシュタインを比較画像にして、アインシュタインに似ている画像をプログラムで見つけます。
この中では年齢と外見からおばあさんの画像が一番近いかと思います。
ソース
# -*- coding: UTF-8 -*- """ pattern_matching.py -version: 3.6.3 -library: opencv """ import cv2 import os def main(): TARGET_FILE = 'einstein.jpg' IMG_DIR = '../images/' bf = cv2.BFMatcher(cv2.NORM_HAMMING) # 特徴点算出のアルゴリズムを決定(コメントアウトで調整して切り替え) # detector = cv2.ORB_create() detector = cv2.AKAZE_create() (target_kp, target_des) = calc_kp_and_des(IMG_DIR + TARGET_FILE, detector) print('TARGET_FILE: %s' % (TARGET_FILE)) files = os.listdir(IMG_DIR) for file in files: if file == '.DS_Store' or file == TARGET_FILE: continue comparing_img_path = IMG_DIR + file try: (comparing_kp, comparing_des) = calc_kp_and_des(comparing_img_path, detector) #画像同士をマッチング matches = bf.match(target_des, comparing_des) dist = [m.distance for m in matches] #類似度を計算する ret = sum(dist) / len(dist) except cv2.error: ret = 100000 print(file, ret) def calc_kp_and_des(img_path, detector): """ 特徴点と識別子を計算する :param str img_path: イメージのディレクトリパス :param detector: 算出の際のアルゴリズム :return: keypoints :return: descriptor """ IMG_SIZE = (200, 200) img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, IMG_SIZE) return detector.detectAndCompute(img, None) if __name__ == '__main__': main()
ソースはほぼまんまこちらを参考にさせていただきました。ありがとうございます。
Python + OpenCVで画像の類似度を求める - Qiita
今回のエラー
コードを試しているときに以下のエラーが出ました。
error: (-215) ssize.width > 0 && ssize.height > 0 in function cv::resize
これはどうやらイメージファイルのパスの指定が間違っていたため、読み込みがうまくできなかったエラーでした。
パスを修正してなんとか回避。
結果
出力は以下のようになりました。
AKAZEの場合
TARGET_FILE: einstein.jpg
girl.jpg 137.53623188405797
man.jpg 132.2753623188406
man2.jpg 154.91304347826087
old_woman.jpg 126.91304347826087
ORBの場合
TARGET_FILE: einstein.jpg
girl.jpg 71.87186629526462
man.jpg 70.72980501392757
man2.jpg 75.0
old_woman.jpg 70.76044568245125
ここで注意したいのは数字が低い方が類似度が高いということになります。
AKAZEの場合はうまいこといっているようです。
ORBの場合は、若干男性のほうが似ているというように出ています。
AKAZEのほうが精度がよいのでしょうか。
特徴点を結びつけたものがこちら
表示させるコードはこちらです。表示させるだけなので大分雑です。
使う場合はリファクタリングしてください。
def show_imgs_match(): img1 = cv2.imread('../images2/einstein.jpg') img2 = cv2.imread('../images2/man3.jpg') gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) akaze = cv2.ORB_create() kp1, des1 = akaze.detectAndCompute(gray1, None) kp2, des2 = akaze.detectAndCompute(gray2, None) bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) matches = bf.match(des1, des2) matches = sorted(matches, key = lambda x:x.distance) img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=2) plt.imshow(cv2.cvtColor(img3, cv2.COLOR_BGR2RGB)) plt.show()