OpenCV pythonでカードのイラスト内に文字を書き込む

# OpenCVのインストール

```
sudo pip install opencv-python
```

# 画像を用意

（例）１．カードのイラスト　blueeyes2.jpg
引用元：[【青眼の白龍（ブルーアイズホワイトドラゴン）の全て】遊戯王最強の称号を持つ伝説のドラゴンの軌跡](https://yu-gi-oh.xyz/青眼の白龍/post-49725/)

![blueeyes2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/24e34fb1-840a-d365-8b14-acb6b9b191af.jpeg)


（例）２．カードのイラストが含まれた画像　blueeyes1.jpg
引用元：[あにてれ](https://ch.ani.tv/episodes/2471)

![blueeyes1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/90ea5537-c442-1c45-c942-d3ab174a07eb.jpeg)

# 画像の左上に文字列を書き込む

OpenCV putTxtを用いる

左上に表示する場合

```py:sample.py
import PIL
from PIL import Image
from PIL import ExifTags
import cv2 as cv
import numpy as np

def write_text(image_path ,x ,y):
    str = "ATK:3000, DEF:2500"
    str_height = 25
    str_width = 340

    img = cv.imread(image_path)

    # 黒塗りの背景を加える                                                                              
    cv.rectangle(img, (x, y), (x+str_width, y-str_height), (0, 0, 0), cv.FILLED, cv.LINE_AA)

    # 線を加える                                                                                        
    cv.rectangle(img, (x, y), (x+str_width, y-str_height), (0, 0, 0), 2, cv.LINE_AA)

    # 文字を加える                                                                                      
    cv.putText(img, str, (x, y),
               cv.FONT_HERSHEY_PLAIN, 2.0,
               (255, 255, 255), 2, cv.LINE_AA)

    cv.imwrite(image_path + '.new.jpg', img)

if __name__ == '__main__':
    write_text('blueeyes1.jpg', 10, 30)
```

実行

```
python sample.py
```

![blueeyes1.jpg.new.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/175b099c-731e-3368-fce2-3be158d770be.jpeg)


# パターンマッチングを組み合わせる場合

以下のコードと組み合わせる

> [特徴点マッチングを用いて、画像からカードのイラストを見つける](https://qiita.com/seigot/items/ca41eceeabfb7765bcc9)

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

fname_img1='blueeyes1.jpg'
fname_img2='blueeyes2.jpg'

img1 = cv2.imread(fname_img1)
img2 = cv2.imread(fname_img2)

def write_text(img ,x ,y):
    str = "ATK:3000, DEF:2500"
    str_height = 25
    str_width = 340

    # 黒塗りの背景を加える
    cv2.rectangle(img, (x, y), (x+str_width, y-str_height), (0, 0, 0), cv2.FILLED, cv2.LINE_AA)

    # 線を加える
    cv2.rectangle(img, (x, y), (x+str_width, y-str_height), (0, 0, 0), 2, cv2.LINE_AA)

    # 文字を加える
    cv2.putText(img, str, (x, y),
               cv2.FONT_HERSHEY_PLAIN, 2.0,
               (255, 255, 255), 2, cv2.LINE_AA)
    cv2.imwrite(fname_img1 + '.new.jpg', img)

def detect(img1, 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)
    
    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)
        
    write_text(img1 ,top_left[0] ,top_left[1])

detect(img1, img2)
```

実行

```
$ $ python sample.py
[[ 6.43984577e-01 -4.58173428e-02  1.61203033e+02]
 [ 2.85230902e-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/4d5aebf2-22f3-f3b9-0475-c11ecebaff44.png)


カードの位置に文字列が出力される
![blueeyes1.jpg.new.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/2d65d497-7a4e-e4df-e670-aa6d79444a84.jpeg)

# Webカメラ画像の場合

<img width="450" alt="yugio3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/4ca939c8-2a6a-64e5-4380-8e0107a0a7e8.png">

![yugio3.png.new.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344880/a1f15486-5e40-2daa-62fc-f257da9815d9.jpeg)

割と見やすくなる。

# 課題
- 他のカードでもマッチングできるようにする
- 文字列をDB等から取得する
- ATK/DEF以外も表示する

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

# 参考
[opencv.jp cv::putText](http://opencv.jp/opencv-2svn/cpp/drawing_functions.html)
[OpenCVで顔認証やってみた（Part2 : 認証編）](https://qrunch.net/@Atom/entries/ybUPbsscInQoonto)
[画像にテキストを書き込む方法](https://axa.biopapyrus.jp/ia/opencv/puttext.html)
[特徴点マッチングを用いて、画像からカードのイラストを見つける](https://qiita.com/seigot/items/ca41eceeabfb7765bcc9)