特徴点マッチングを用いて、画像からカードのイラストを見つける

# 目的
特徴点マッチングを用いて、画像からカードのイラストを見つける際の備忘録です。

# 準備

１．カードのイラスト
２．カードのイラストが含まれた画像
３．カードのイラストが含まれた画像から、カードのイラストを見つけるコード

（例）１．カードのイラスト　test2.png
引用元：[【青眼の白龍（ブルーアイズホワイトドラゴン）の全て】遊戯王最強の称号を持つ伝説のドラゴンの軌跡](https://yu-gi-oh.xyz/青眼の白龍/post-49725/)
![blueeyes2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/9de9efa2-d87b-1b3e-9890-2ade2483f8fc.jpeg)

（例）２．カードのイラストが含まれた画像　test1.png
引用元：[あにてれ](https://ch.ani.tv/episodes/2471)
![blueeyes1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/0e29df72-7a41-a644-7c8b-0b09c4a278a2.jpeg)

# ３．カードのイラストが含まれた画像から、カードのイラストを見つけるコード

特徴点を算出して、画像からカードのイラストを抽出するコード
参考：[ミッキーを特徴点マッチングで検出する](https://qiita.com/seigot/items/b19ff58c90381665ca3d)

特徴点は[AKAZE](http://pwiki.awm.jp/~yoya/?AKAZE)を使用します。



```py:sample.py
import cv2
import numpy as np

fname_img1='test1.png'
fname_img2='test2.png'

img1 = cv2.imread(fname_img1)
img2 = cv2.imread(fname_img2)
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
akaze = cv2.AKAZE_create()
kp1, des1 = akaze.detectAndCompute(gray1, None)
kp2, des2 = akaze.detectAndCompute(gray2, None)

#img1_sift = cv2.drawKeypoints(gray1, kp1, None, flags=4)
#img2_sift = cv2.drawKeypoints(gray2, kp2, None, flags=4)
#cv2.imwrite('out1.png', img1_sift)
#cv2.imwrite('out2.png', img2_sift)

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)
cv2.imwrite('out-match.png', img3)

#homography
good_match_rate = 0.15;
good = matches[:int(len(matches) * good_match_rate)]

min_match=10
if len(good) > min_match:
    src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
    dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
    # Find homography
    M, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC)
    matchesMask = mask.ravel().tolist()

    print(M)
    print(M[0][2])
    print(M[1][2])

    height = img2.shape[0]
    width = img2.shape[1]
    top_left = (int(M[0][2] +0.5), int(M[1][2] +0.5)); #tx,ty
    bottom_right = (top_left[0] + width, top_left[1] + height)

#result
result = cv2.imread(fname_img1)
cv2.rectangle(result,top_left, bottom_right, (255, 0, 0), 10)
cv2.imwrite("result.png", result)
```

# テスト

実行結果

```
$ python sample.py
[[ 6.43984577e-01 -4.58173428e-02  1.61203033e+02]
 [ 2.85![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/23ae3f02-409a-6a3a-5e8b-673a20afa502.png)
230902e-02  4.12312421e-01  1.50286636e+02]
 [ 4.54533377e-04 -5.75705548e-04  1.00000000e+00]]
161.2030333928222
150.28663624674783
```

特徴点を算出してマッチング

![out-match.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/7fabe252-b74b-7d25-b881-b0c8844f68b8.png)

枠を検出
※カードのサイズ変化を考慮できておらず、枠が少し大きめになっています

![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/b392a2a2-a6f3-14b0-1180-4b08bda58e33.png)

# Webカメラ画像の場合（１枚のカード）

1枚ならば検出できそう。

![out-match.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/346786ad-6d44-9782-0a8f-8c93fe0e6f38.png)
![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/5a36ae1a-94af-b551-9071-16f12eb82076.png)

枠ズレは要調整(今は固定値)

# Webカメラ画像の場合（複数枚のカード）

複数枚は厳しい。

![out-match.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/a899c171-567d-39cd-58b4-2c813cd3a70d.png)
![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/38e8b6f6-ba40-e0b7-331d-9e0d7519cb10.png)

# 課題
 - 複数枚のカード対応
 - 動画対応
 - 枠ズレ調整

遊戯王のリモートデュエルに使えるかもしれません。

# CodingError対策
特になし

# 参考
[【青眼の白龍（ブルーアイズホワイトドラゴン）の全て】遊戯王最強の称号を持つ伝説のドラゴンの軌跡](https://yu-gi-oh.xyz/青眼の白龍/post-49725/)
[あにてれ](https://ch.ani.tv/episodes/2471)
[ミッキーを特徴点マッチングで検出する](https://qiita.com/seigot/items/b19ff58c90381665ca3d)
[AKAZE](http://pwiki.awm.jp/~yoya/?AKAZE)