特徴点のマッチングと対応点の座標のCSV出力
前回記事ではAkaze特徴点を抽出し、CSVファイルに書き出す方法を紹介しました。
今回は特徴点を抽出した2枚の画像に対して、特徴点のマッチングを行う方法を紹介します。
開発環境はjupyter notebook、言語はpythonです。
今回使用した画像・コード一式はgoogle driveにも保存しておりますので、必要に応じてお使いください。
作成したコード
#モジュールのインポート import cv2 import glob import matplotlib.pyplot as plt import numpy as np import pandas as pd import os %matplotlib inline #検出器の生成 akaze = cv2.AKAZE_create() filepath_train = glob.glob("train/*.png") filepath_query = glob.glob("query/*.png") bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) df = pd.DataFrame(columns=['x_query','y_query','x_train','y_train','Distance','img_index']) for num in range((len(filepath_train))): img_train = cv2.imread(filepath_train[num]) img_query = cv2.imread(filepath_query[num]) kp_train, des_train = akaze.detectAndCompute(img_train, None) kp_query, des_query = akaze.detectAndCompute(img_query, None) img_train_key = cv2.drawKeypoints(img_train, kp_train, None, flags=4) img_query_key = cv2.drawKeypoints(img_query, kp_query, None, flags=4) matches = bf.match(des_train, des_query) matches = sorted(matches, key = lambda x:x.distance) for i in range(len(matches)): df.loc["Matches"+str(i)] = [kp_train[matches[i].queryIdx].pt[0],kp_train[matches[i].queryIdx].pt[1],kp_query[matches[i].trainIdx].pt[0],kp_query[matches[i].trainIdx].pt[1],matches[i].distance,matches[i].imgIdx] df.to_csv("Matches"+os.path.split(filepath_train[num])[1][:-4]+"_"+os.path.split(filepath_query[num])[1][:-4]+".csv") img_match = cv2.drawMatches(img_train, kp_train, img_query, kp_query, matches[:10], None, flags=2) cv2.imwrite(os.path.split(filepath_train[num])[1][:-4]+"_"+os.path.split(filepath_query[num])[1][:-4]+"_keypoint.png",img_match)
処理の解説
1.特徴点のマッチング
今回はBrute-Force matcher(総当たりマッチング)を使用します。まず特徴点をマッチングさせる際は、BFMatcher型のオブジェクトを下記コマンドで生成します。
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)このオブジェクトを使って、detectAndCompute()で検出した特徴点を、次のコマンドによってマッチングさせます。
matches = bf.match(des_train, des_query)また任意の処理ではありますが、今回はマッチングさせた特徴点の組み合わせから、マッチング精度の高いもの(ユークリッド距離の短いもの)を降順に並び替えます。
matches = sorted(matches, key = lambda x:x.distance)次項ではこの処理によってマッチした結果を書き出す処理について紹介します。
2.対応点の書き出し
CSVファイルへの書き出し
まずマッチングした特徴点の組み合わせから、各特徴点の座標、マッチング時の類似度(ユークリッド距離)、画像のインデックスを取得し、pandasのデータフレームに追加します。for i in range(len(matches)): df.loc["Matches"+str(i)] = [kp_train[matches[i].queryIdx].pt[0],kp_train[matches[i].queryIdx].pt[1],kp_query[matches[i].trainIdx].pt[0],kp_query[matches[i].trainIdx].pt[1],matches[i].distance,matches[i].imgIdx] df.to_csv("Matches"+os.path.split(filepath_train[num])[1][:-4]+"_"+os.path.split(filepath_query[num])[1][:-4]+".csv")
ここでi番目の組み合わせからtrain画像の特徴点を取り出す場合には、kp_train(matches[i].queryIdx)と書き、query画像の特徴点を取り出す場合は、kp_query(matches[i].trainIdx)と書くことに注意してください。
書き出したcsvファイルのうち、上位の結果を取り出したものが、次の表となります。
これにより各特徴点の座標を自動で取得できたことが確認されました。
画像への書き出し
次にマッチングした特徴点同士を線で結び、視覚的に表した画像を生成します。今回はtrainとqueryで2枚の画像を用意しています。
特徴抽出ではイラストのような特異的な模様の方が上手くいく傾向にあるため、ドラえもんのマスキングテープの撮影写真を用意してみました。
train画像
query画像
その際使用するコマンドは次の通りです。
img_match = cv2.drawMatches(img_train, kp_train, img_query, kp_query, matches[:10], None, flags=2)今回はmatches[:10]とすることで、表示する線を10本に絞っています。
ここでmatches[:10]をmatchesに書き換えてしまうと、全ての特徴点の組み合わせを線で結んでしまうため、ある程度線の数を抑える方がおすすめです。
一応matchesに書き換えた状態で画像を出力した結果を図に示します。
あまりにもたくさんの線が引かれているため、いまいち合っているのか間違っているのか分かりませんが、画像下部から斜めに伸びた線など、明らかに対応しない組み合わせも存在しています。
一方、表示を10本に絞ると次のようになります。
なんとなく対応している点同士が線で結びついていることが見て取れます。
例えばこの画像の中の歌うドラえもんに着目してみましょう。
左側の画像ではひげと右足に特徴点が描かれています。
train画像 拡大図
次に右側の画像を見ると、まったく同じ箇所に特徴点が描かれていることが分かります。
それぞれの画像でドラえもんのスケールに違いがありますが、問題なく特徴点のマッチングができています。
query画像 拡大図
特徴点の検出およびマッチングは、オルソフォトの作成や画像を用いた3次元形状の復元など、様々な技術に活用されています。
これは初歩の初歩ですが、ぜひ技術を磨いてみてください。
今回使用した画像:ドラえもんマスキングテープ
藤子・F・不二雄ミュージアムで購入したマスキングテープを使用させていただきました。
老若男女問わず楽しめる施設になってます。
皆さんぜひ行ってみてください。※要予約である点に注意
コメント
コメントを投稿