Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
13
Help us understand the problem. What is going on with this article?
@yu_haruchan

画像の特徴点情報をファイルに保存してマッチングに利用する方法

More than 1 year has passed since last update.

はじめに

OpenCVの特徴点抽出及びマッチングを用いる機会が多いのですが、
複数画像を用いる場合、特徴点抽出に時間がかかることがよく起こりました。
そこで今回は特徴点情報をファイルに出力し、マッチングに利用するプログラムを作りました。

今回は例として、女の子たち(12枚のソース画像)から赤めがねの女の子(ターゲット画像)を見つけてみましょう!

ソース画像⬇︎
youngwoman_37.jpgyoungwoman_38.jpgyoungwoman_39.jpgyoungwoman_40.jpg

youngwoman_41.jpgyoungwoman_42.jpgyoungwoman_43.jpgyoungwoman_44.jpg

youngwoman_45.jpgyoungwoman_46.jpgyoungwoman_47.jpgyoungwoman_48.jpg

ターゲット画像⬇︎
target_girl.jpg

開発環境

今回は以下の環境で開発しました。
OpenCV 4.1.2
Python 3.8.1

構成

以下プログラムの流れです。次節から順を追って解説します。
1. ソース画像の特徴点情報を取得しファイルに保存
2. ターゲット画像の特徴点情報を取得
3. ファイルからソース画像の特徴点情報を取得
4. マッチング

1. ソース画像の特徴点情報を取得しファイルに保存

まずはsave_features.pyにて、ソース画像(女の子たち)の特徴点情報を取得しファイルに保存します。
今回はOpenCVに実装されている特徴点記述子、AKAZEを用いました。
keypointをファイル保存するにあたり、cv::KeyPointにアクセスしてリスト化する必要があります。

save_features.py
# 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型に変換しました。

save_features.py
# keypointをbytesに変換
map(bytes, keypoints)

以下、ソースコード全体です。

save_features.py
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を用いて特徴点情報を取得します。

get_features_from_file.py
# ターゲット画像読み込み
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に変換し元の構造に戻します。

get_features_from_file.py
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に設定)以上であればマッチング成功とします。

get_features_from_file.py
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,
        )

以下、ソースコード全体です。

get_features_from_file.py
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()

結果

以下の結果画像では、ソース画像とターゲット画像とでマッチングした特徴点同士を描画しています。
無事ターゲットの女の子を見つけることができました!
image.png

終わりに

特徴点をファイルに保存する肝は、
cv::KeyPointにアクセスすること
アクセスした情報をもとにリスト化すること
です。
OpenCVで画像処理する機会があればぜひ試してみてください。

参考ページ

Python3版OpenCVのKeyPointをファイルに書き出す

python3,opencv3で特徴点マッチング(AKAZE, KNN)

13
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
yu_haruchan
埼玉県川越市出身。 大学では、経営工学を専攻。 ヒューマン・インタフェースを専門とする研究室に所属し、学部・修士で生体認証における画像認識を研究テーマとする。 タビアンには、画像処理のエンジニアとして参画。 大学在学中。
tabian
画像認識技術を用いて、新しいポスター体験を提供する「ポ写」を開発している全員副業のスタートアップです! エンジニアが自由に、でも真面目に技術に向き合える環境、それは最新技術にとことんこだわれることだと思って日々Qiitaの更新頑張ってます。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
13
Help us understand the problem. What is going on with this article?