こんにちは。堤 真聖です。
この間ショッピングモールに出かけたらみんなポケモンGOをやっていて驚きました。
ポケモンGO、1週間だけやって辞めたけど、まだ結構流行っているんですね。
せっかくだしポケモンの懐かしさに浸りつつ「自分に似てるポケモンを抽出してくれる解析プログラム」でも作ってみようかな!
- openCVで画像を取得
- openCVで画像を調整
- openCVで特微量計算
- openCVで類似度判定
思いっきりopenCVの力を借りて実装して行きます!
使用画像
今回810種類のポケモンと闘わせる画像はコチラ。
田舎のヤンキーみたいな写真しかなくてごめんなさい。
まずは特微量を算出。
コードはGitHubにアップしているので要所をかいつまんで行きます。
import cv2
import os
def calc_feature(img_path: str, detector: cv2.ORB_create()):
IMG_SIZE = (100, 100)
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, IMG_SIZE)
return detector.detectAndCompute(img, None)
次に、810もある画像一つ一つを比較し特微量の差を計算します
target_image = 'me.jpg'
image_directory = '../../blog/material_images/'
bf = cv2.BFMatcher(cv2.NORM_HAMMING)
detector = cv2.AKAZE_create()
(target_kp, target_des) = calc_feature(image_directory + target_image, detector)
min_value = 1000
min_path = ''
files = os.listdir(image_directory)
for file in files:
if file == '.DS_Store' or file == target_image:
continue
comparing_img_path = image_directory + file
try:
(comparing_kp, comparing_des) = calc_feature(
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 = 1000
if min_value > ret:
min_value = ret
min_path = file
こうなります。
結果
target: me.jpg
parasect.png 165.36363636363637
lumineon.png 157.27272727272728
.
.
.
latios.png 151.63636363636363
mew.png 170.36363636363637
ekans.png 173.9090909090909
今回計算しているのは「特微量の差」なので数値が低いほど類似度が高いということになります。
どれも少し値が大きすぎますね。もしかして俺はポケモンには似てないってことかな(笑)。
もう少し詰めて行きます。
アルゴリズムをORBに変えてみます。
出力結果
target: me.jpg
parasect.png 87.61702127659575
lumineon.png 81.08510638297872
.
.
.
latios.png 71.34042553191489
mew.png 86.91489361702128
ekans.png 86.68085106382979
AKAZEの半分ほどの値になりましたね。
今思ったけど自分からポケモンに似せていくってなんかシュール。
それでは結果を可視化して行きます。
def show_imgs_match(file):
img1 = cv2.imread('../../blog/material_images/me.jpg')
img2 = cv2.imread('../../blog/material_images/' + file)
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()
さて、ここで問題です。俺はどのポケモンに似ているでしょうか?(810択クイズ)
正解は、、、
デンヂムシ!
参考:デンヂムシ ポケットモンスターサン・ムーン公式サイト
確かになんか似ている。。
値が62程度まで落ち着きました。
ちなみにAKAZEでは
カプ・レヒレ!
参考:カプ・レヒレ ポケットモンスターサン・ムーン公式サイト
ちなみにこちらの場合、値は100前後になりました。
AKAZEとは、要は画素の応答値を閾値と比較し最大値であればと特微点とする方法。なので出っ張った鼻やズボンの角などのエッジ部分が特に紐づいています。
詳しい非線形拡散方程式の計算方法は以下より。
Qlita-KAZE理論を読んでみた
対してORBは、輝度値やエッジを特微点として判定するため、同じような色合いを紐付けます。
つまり、ポケモンは「絵」なので、凹凸よりも輝度やエッジによる特徴が特微点として検出されやすいということ。
なので今回はAKAZEよりもORB方が類似度が高かったんですね。
要するに、AKAZEの場合、同じような形のポケモンが、ORBの場合、同じような色彩のポケモンが高い類似度だと判定されるということなんですね。
今後の展望
- 画像の処理の精度を高める
- 特微量算出アルゴリズムの工夫
さらに精度を高めてデプロイするので楽しみにしていてください!
参照
ソースコード:Github-masatsch/find-similar-pokemon
参考:特徴点のマッチング
今回の記事では画像認識の技術を利用して似ているポケモンを探しました。AI、機械学習それからディープラーニングの経験はないけど、「AIをどのように活用するのだろう?」や「Aidemy Premium Planを受講して、私でもきちんと続けられるだろうか?」など、少しでも気になることがございましたら、ぜひ一度気軽に無料相談会にお越しいただき、お悩みをお聞かせください!
最後までご覧くださりありがとうございました。