はじめに
OpenCVの特徴点抽出及びマッチングを用いる機会が多いのですが、
複数画像を用いる場合、特徴点抽出に時間がかかることがよく起こりました。
そこで今回は特徴点情報をファイルに出力し、マッチングに利用するプログラムを作りました。
今回は例として、女の子たち(12枚のソース画像)から赤めがねの女の子(ターゲット画像)を見つけてみましょう!
開発環境
今回は以下の環境で開発しました。
OpenCV 4.1.2
Python 3.8.1
##構成
以下プログラムの流れです。次節から順を追って解説します。
- ソース画像の特徴点情報を取得しファイルに保存
- ターゲット画像の特徴点情報を取得
- ファイルからソース画像の特徴点情報を取得
- マッチング
1. ソース画像の特徴点情報を取得しファイルに保存
まずはsave_features.pyにて、ソース画像(女の子たち)の特徴点情報を取得しファイルに保存します。
今回はOpenCVに実装されている特徴点記述子、AKAZEを用いました。
keypoint
をファイル保存するにあたり、cv::KeyPoint
にアクセスしてリスト化する必要があります。
# keypointをlist化
keypoint = []
for p in features[0]:
temp = (p.pt, p.size, p.angle, p.response, p.octave, p.class_id)
keypoint.append(temp)
また、今回はメモリの消費量を抑えるために特徴点情報をbyte型に変換しました。
# keypointをbytesに変換
map(bytes, keypoints)
以下、ソースコード全体です。
import cv2 as cv
import pickle
SOURCE_FILES = [
"youngwoman_37.jpg",
"youngwoman_38.jpg",
"youngwoman_39.jpg",
"youngwoman_40.jpg",
"youngwoman_41.jpg",
"youngwoman_42.jpg",
"youngwoman_43.jpg",
"youngwoman_44.jpg",
"youngwoman_45.jpg",
"youngwoman_46.jpg",
"youngwoman_47.jpg",
"youngwoman_48.jpg",
]
def get_features(img_file_name):
"""Get features of master images
Args:
img_file_name(list): Master image
Returns:
keypoints, descriptors, img
"""
img = cv.imread("images/" + img_file_name)
# 特徴点情報抽出
akaze = cv.AKAZE_create()
kp, des = akaze.detectAndCompute(img, None)
features = [kp, des]
return features
sources = {}
for item in SOURCE_FILES:
features = get_features(item)
# keypointをlist化
keypoints = []
for p in features[0]:
temp = (p.pt, p.size, p.angle, p.response, p.octave, p.class_id)
keypoints.append(temp)
# keypointsをbytesに変換
map(bytes, keypoints)
# 特徴点情報を辞書化
sources[item] = {
"src": item,
"keypoint": keypoints,
"descriptor": features[1],
}
# 特徴点情報をファイルに書き込み
with open("sources_data.pickle", mode="wb") as f:
pickle.dump(sources, f)
2.ターゲット画像の特徴点情報を取得
手順2からget_features_from_file.pyにて処理を行なっています。
ソース画像同様、特徴点記述子であるAKAZEを用いて特徴点情報を取得します。
# ターゲット画像読み込み
target_img = cv.imread("images/target_girl.jpg")
# 特徴量取得
akaze = cv.AKAZE_create()
target_keypoint, target_descriptor = akaze.detectAndCompute(target_img, None)
3.ファイルからソース画像の特徴点情報を取得
get_sources()
でファイルから特徴点情報を取得します。
pickle化するためにkeypoints
をbyte化したので、listに変換し元の構造に戻します。
def get_sources():
"""Get source's features from file
Returns:
sources(list): source's keypoints, descriptors,and img
"""
# 特徴点情報をファイルから取得
with open("sources_data.pickle", mode="rb") as f:
sources = pickle.load(f)
for n in sources:
items = sources[n]
# keypointsをbytesからlistに直す
list(map(list, items["keypoint"]))
# keypointsを元の構造に復元
keypoints = []
for p in items["keypoint"]:
temp = cv.KeyPoint(
x=p[0][0],
y=p[0][1],
_size=p[1],
_angle=p[2],
_response=p[3],
_octave=p[4],
_class_id=p[5],
)
keypoints.append(temp)
items["keypoint"] = keypoints
return sources
4.マッチング
ソース画像ごとにターゲット画像の特徴点情報とマッチングを行います。
データを間引きし、マッチングした特徴点の数が設定した閾値(今回は20に設定)以上であればマッチング成功とします。
for n in sources:
source = sources[n]
source_img = cv.imread("images/" + source["src"])
matches = matcher.knnMatch(source["descriptor"], target_des, k=2)
# データを間引きする
ratio = 0.5
matched_keypoints = []
for m, n in matches:
if m.distance < ratio * n.distance:
matched_keypoints.append([m])
# 任意の閾値よりgoodが多い場合結果画像を出力
if len(matched_keypoints) > 20:
out = cv.drawMatchesKnn(
source_img,
source["keypoint"],
target_img,
target_kp,
matched_keypoints,
None,
flags=2,
)
以下、ソースコード全体です。
import cv2 as cv
import pickle
def get_sources():
"""Get source's features from file
Returns:
sources(list): source's keypoints, descriptors,and img
"""
# 特徴点情報をファイルから取得
with open("sources_data.pickle", mode="rb") as f:
sources = pickle.load(f)
for n in sources:
items = sources[n]
# keypointsをbytesからlistに直す
list(map(list, items["keypoint"]))
# keypointsを元の構造に復元
keypoints = []
for p in items["keypoint"]:
temp = cv.KeyPoint(
x=p[0][0],
y=p[0][1],
_size=p[1],
_angle=p[2],
_response=p[3],
_octave=p[4],
_class_id=p[5],
)
keypoints.append(temp)
items["keypoint"] = keypoints
return sources
matcher = cv.BFMatcher()
# ターゲット画像読み込み
target_img = cv.imread("images/target_girl.jpg")
# 特徴量取得
akaze = cv.AKAZE_create()
target_kp, target_des = akaze.detectAndCompute(target_img, None)
# ソース画像の特徴点情報をファイルから読み込み
sources = get_sources()
for n in sources:
source = sources[n]
source_img = cv.imread("images/" + source["src"])
matches = matcher.knnMatch(source["descriptor"], target_des, k=2)
# データを間引きする
ratio = 0.5
matched_keypoints = []
for m, n in matches:
if m.distance < ratio * n.distance:
matched_keypoints.append([m])
# 任意の閾値よりgoodが多い場合結果画像を出力
if len(matched_keypoints) > 20:
out = cv.drawMatchesKnn(
source_img,
source["keypoint"],
target_img,
target_kp,
matched_keypoints,
None,
flags=2,
)
cv.imwrite("images/result.jpg", out)
cv.waitKey()
結果
以下の結果画像では、ソース画像とターゲット画像とでマッチングした特徴点同士を描画しています。
無事ターゲットの女の子を見つけることができました!
終わりに
特徴点をファイルに保存する肝は、
cv::KeyPoint
にアクセスすること
アクセスした情報をもとにリスト化すること
です。
OpenCVで画像処理する機会があればぜひ試してみてください。
参考ページ
Python3版OpenCVのKeyPointをファイルに書き出す
[python3,opencv3で特徴点マッチング(AKAZE, KNN)]
(https://techtech-sorae.com/python3opencv3%E3%81%A7%E7%89%B9%E5%BE%B4%E7%82%B9%E3%83%9E%E3%83%83%E3%83%81%E3%83%B3%E3%82%B0akaze-knn/)