この記事は リンク情報システム の「2020新春アドベントカレンダー TechConnect!」のリレー記事です。
TechConnect! は勝手に始めるアドベントカレンダーとして、engineer.hanzomon という勝手に作ったグループによってリレーされます。
(リンク情報システムのFacebookはこちらから)
テーマが自由だったので自由な記事となります。(使用技術は真面目です。)
チュートリアルを駆使し、頑張って画像認識をしました。
出来たもの
Shadowverse(Steam版)のWin/Lose判定を行うプログラムが出来ました。
(gifの画面はLose判定を確認するために描画しています。判定は描画しなくても可能です。)
見ての通り、Win/Lose画像とキャプチャ画面を比較して判定しているだけなので、
「特定のアプリケーションのキャプチャに、指定画像が出現しているかをリアルタイムで判定する」
ことがしたい人の参考にもなるかと思います。
使ったもの
- Python 3.7.4
- pip 19.3.1
- numpy 1.17.3
- opencv-contrib-python 4.1.1.26
- Pillow 6.2.0
導入方法は後述する参考記事を参照してください。
片っ端からpipでインストールするだけで出来たので特に詰まった所は無かったです。
やり方
- ゲーム画面をスクリーンキャプチャする
- キャプチャした画像と、Win/Lose画像を比較
- 特徴点をDistanceで足切りし、一定以上の特徴点がマッチしていればWin/Loseと判断
- 一定回数以上Win/Lose判定が連続した場合に、Win画面/Lose画面と判定
ゲーム画面をスクリーンキャプチャする
以下の記事を参考にPILを使用してキャプチャを行いました。
[Python][Windows] Pythonでスクリーンキャプチャを行う
from PIL import ImageGrab
import numpy as np
TARGET_NAME = 'Shadowverse'
handle = win32gui.FindWindow(None, TARGET_NAME)
while True:
rect = win32gui.GetWindowRect(handle)
img = ImageGrab.grab(rect)
ocv_im = np.asarray(img)
#OpenCV用に色変換
ocv_im = cv2.cvtColor(ocv_im, cv2.COLOR_BGR2RGB)
#画面に描画(確認用)
cv2.imshow("images", ocv_im, )
#適当な方法でWaitをかける(描画時間の確保)
cv2.waitKey(10)
##キャプチャした画像と、Win/Lose画像を比較
(あらかじめWin/Lose画像はスクリーンショットから切り抜いておきます。)
以下の記事を参考に、AKAZEで特徴量マッチングを行います。
OpenCV 3とPython 3で特徴量マッチング(A-KAZE, KNN)
def MatchResultCheck(ocv_img):
win_img = cv2.imread(WIN_IMAGE_PATH)
lose_img = cv2.imread(LOSE_IMAGE_PATH)
akaze = cv2.AKAZE_create()
kp2, des2 = akaze.detectAndCompute(ocv_img, None)
kp1_l, des1_l = akaze.detectAndCompute(lose_img, None)
kp1_w, des1_w = akaze.detectAndCompute(win_img, None)
is_win = False
is_lose = False
bf = cv2.BFMatcher()
if not des2 is None :
#Lose画像とキャプチャ画像で特徴量マッチング
matches_l = bf.knnMatch(des1_l,des2, k=2)
#Win画像とキャプチャ画像で特徴量マッチング
matches_w = bf.knnMatch(des1_w,des2, k=2)
#Lose判定
good_l = []
for match1, match2 in matches_l:
if match1.distance < 0.75*match2.distance:
good_l.append([match1])
#Win判定
good_w = []
for match1, match2 in matches_w:
if match1.distance < 0.75*match2.distance:
good_w.append([match1])
#確認用の画像を作成
akaze_matches = cv2.drawMatchesKnn(lose_img,kp1_l,ocv_img,kp2,good_l,None,flags=2)
#画面に描画(確認用)
cv2.imshow("match", akaze_matches, )
if len(good_l) > 20 :
print("is lose")
is_lose = True
if len(good_w) > 20 :
print("is win")
is_win = True
return is_win, is_lose
###Win/Loseの判定
マッチング結果にはDistance(どれだけマッチしているか)が格納されているので、Distanceで足切りを行います。
#Lose判定
good_l = []
for match1, match2 in matches_l:
#Distanceが一定以上の特徴点だけを抜き出す
if match1.distance < 0.75*match2.distance:
good_l.append([match1])
足切り後、一定以上特徴点が残っていれば、Win/Lose画像が存在していると判断します。
if len(good_l) > 20 :
print("is lose")
is_lose = True
##Win画面/Lose画面の判定
誤検知を抑止するために、一定回数以上連続でWin/Lose画像と判断した場合のみWin/Lose画面と判定します。
(python使ってるんだからもっと簡素に書けそう……)
is_win, is_lose = MatchResultCheck(ocv_im)
if is_lose :
cnt_lose_match += 1
if cnt_lose_match >= 4:
print("is lose match")
cnt_lose_match = 0
cv2.waitKey(1000)
else :
cnt_lose_match = 0
#出来なかったこと
同様に先攻/後攻も判定しよう!と思ったら上手く出来ませんでした……
Win/Loseと違って判定画像が小さすぎるのと、漢字なのが辛いのかなって思ってます。
引き続き精進していきます。
#まとめ
スクリーンキャプチャに特定の画像が存在しているか判定できました。
判定結果をリアルタイムに描画できると面白いですね。
次回は@rysk001さんです。