#前書き
OpenCVの開発環境は作ったので、実際になにか動かしてみます。
今回はOpenCV-Pythonのチュートリアル、OpenCV-Python Tutorialsの**日本語版**をやっていきます。
多少アレンジすると思う。
物体認識をやっていきたいので、とりあえず簡単なところから練習していきます。
シリーズ
#開発環境
機種名 : MacBook Pro
OS : Mojave 10.14.2
プロセッサ名 : Intel Core i5 2.6 GHz
メモリ : 16 GB
グラフィック : Intel Iris 1536MB
python系
% pip freeze
numpy==1.15.4
opencv-python==4.0.0.21
画像データ
今回はこの三枚の画像を使っていきます。
#本題
画面下部のそれぞれのドロップを認識することを目標とします。
課題
- 丸ドロップと四角ドロップを別物として認識する(2パターン:6×5盤面)
- 丸ドロップが別物として認識する(5パターン:6×5盤面)
- 5×4盤面、6×5盤面、7×6盤面で認識する
丸ドロップと四角ドロップを別物として認識する(2パターン:6×5盤面)
とりあえず2値化
import numpy as np
import cv2
# 画面下部のボードだけ取得
def getBoard(img):
x1 = 0
x2 = 750
y1 = 650
y2 = 1334
return img[y1:y2, x1:x2]
# B:0,G:1,R:2
def getChanel(img, flag):
b, g, r = cv2.split(img)
if flag == 0:
return b
elif flag == 1:
return g
elif flag == 2:
return r
else:
raise ValueError("No Flag")
# 2値化
def getBinary(img):
_, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
return thresh
def main():
# 画像入力
img = cv2.imread('6_5.PNG')
# 盤面情報取得
board = getBoard(img)
# グレースケール画像取得
blue = getChanel(board, 0)
# 2値化
binary = getBinary(blue)
# 表示
# cv2.imshow('img', img)
# cv2.imshow('board', board)
# cv2.imshow('blue', blue)
cv2.imshow('binary', binary)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
try:
main()
except ValueError as e:
print("type:{0}".format(type(e)))
print("args:{0}".format(e.args))
print("message:{0}".format(e.message))
print("{0}".format(e))
これ、かなり手順踏んでやってるけど、cv2.imread('6_5.PNG',0)
でグレースケール画像取れるの忘れてた。
- cv2.IMREAD_COLOR : カラー画像として読み込む.画像の透明度は無視される.デフォルト値
- cv2.IMREAD_GRAYSCALE : グレースケール画像として読み込む
- cv2.IMREAD_UNCHANGED : アルファチャンネルも含めた画像として読み込む
これが使える画像なのかはわからないけど、、、四角か丸かぐらいは判断できるでしょう。
ハフ変換による円形抽出
前回の記事で「ハフ変換による円検出」というのをやったので、今回のドロップ検出にも適応できるかなと。
※先に言っておくと、検出数やパラメータを適切にしてないとものすごい時間かかるから気をつけてね。
ハフ変換による円検出こちらの記事をそのまま適応した画像がこちら。
全然わからんw
中心点だけピックアップしても600個近く。
ハフ変換のパラメータを調整してみる。
- image – 8ビット,シングルチャンネル,グレースケールの入力画像.
- circles – 検出された円を出力するベクトル.各ベクトルは,3要素の浮動小数点型ベクトル (x, y, radius) としてエンコードされます.
- method – 現在のところ, CV_HOUGH_GRADIENT メソッドのみが実装されています.基本的には 2段階ハフ変換 で,これについては Yuen90 で述べられています.
- dp – 画像分解能に対する投票分解能の比率の逆数.例えば, dp=1 の場合は,投票空間は入力画像と同じ分解能をもちます.また dp=2 の場合は,投票空間の幅と高さは半分になります.
- minDist – 検出される円の中心同士の最小距離.このパラメータが小さすぎると,正しい円の周辺に別の円が複数誤って検出されることになります.逆に大きすぎると,検出できない円がでてくる可能性があります.
- param1 – 手法依存の 1 番目のパラメータ. CV_HOUGH_GRADIENT の場合は, Canny() エッジ検出器に渡される2つの閾値の内,大きい方の閾値を表します(小さい閾値は,この値の半分になります).
- param2 – 手法依存の 2 番目のパラメータ. CV_HOUGH_GRADIENT の場合は,円の中心を検出する際の投票数の閾値を表します.これが小さくなるほど,より多くの誤検出が起こる可能性があります.より多くの投票を獲得した円が,最初に出力されます.
- minRadius – 円の半径の最小値.
- maxRadius – 円の半径の最大値.
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 100,
param1=50, param2=30, minRadius=60, maxRadius=70)
とりあえず、minDistを100、minRadiusを60、maxRadiusを70にして画像を再出力してみる。
思ったよりきっちり取れててびっくり。
回復ドロップのマッチング
# 盤面取得
img_rgb = cv2.imread('6_5.PNG')
img_rgb = getBoard(img_rgb)
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
# ドロップ取得
template = img_gray[85:170, 635:730]
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(img_gray, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
cv2.imshow('img_rgb', img_rgb)
cv2.imwrite('res.png', img_rgb)
cv2.waitKey(0)
cv2.destroyAllWindows()
攻撃色と回復色の認識がとりあえずできた。
# 盤面取得
img_rgb = cv2.imread('6_5.PNG')
img_rgb_board = getBoard(img_rgb)
img_gray = cv2.cvtColor(img_rgb_board, cv2.COLOR_BGR2GRAY)
# 攻撃色
img = cv2.medianBlur(img_gray, 5)
circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 100,
param1=50, param2=30, minRadius=60, maxRadius=70)
# 攻撃色に縁取り
circles = np.uint16(np.around(circles))
index = 1
for i in circles[0, :]:
index = index + 1
cv2.circle(img_rgb_board, (i[0], i[1]), i[2], (0, 255, 0), 2)
cv2.circle(img_rgb_board, (i[0], i[1]), 2, (0, 0, 255), 3)
# 回復ドロップ取得
template = img_gray[80:170, 635:730]
cv2.imwrite('template.png', template)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)
# 回復ドロップに縁取り
for pt in zip(*loc[::-1]):
cv2.rectangle(img_rgb_board, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
cv2.imshow('img_rgb', img_rgb_board)
cv2.imwrite('res.png', img_rgb_board)
cv2.waitKey(0)
cv2.destroyAllWindows()
攻撃色がいまいち綺麗に抜けてないけど、ひとまず課題1を達成することができた。
(右下の青ドロップとか。)
#感想
とりあえず課題1を達成することができました。
複雑なコード書かずにこの程度のことはできることがわかった。