yasudacloudの日記

札幌に住むソフトウェアエンジニア

OpenCVで将棋の局面を読み取る(読み取りたい)

前回の続き。

書籍で学べることを紹介し、実際にやってみたいと思ってた将棋盤と駒の検出をやってみたいと思います。

 

yasuda.cloud

OpenCVではじめよう ディープラーニングによる画像認識:書籍案内|技術評論社

第6章 ディープラーニング

とうとうディープラーニングデビュー。

6章は実装(コード)は少なく、6.1、6.2でディープラーニングに関する考え方、用語、精度等について記述されています。この本が素晴らしいのは、例題が丁寧に書かれているので難しい数式を全て理解出来なくても内容が入ってきやすいという点です。

個人的には6.3 画像処理におけるディープラーニング のCNNに関する記述の情報価値が高いと感じました。

第7章 dnnモジュール基礎

本章が最もページ数が多く、内容が濃くて難易度も高めでした。(まだ読み切ってないですが..)

推論の記述でLow Level APIの実装が難しく、何度も読み直して時間を費やしてしまいました。後のHigh Level API(Low Levelより優しい)が本章のメインだったことに気づきホッとします😅

High Level APIは具体的に顔検出、オブジェクト検出、テキスト検出について書かれており、学習済みモデルやモデルの入力パラメータについても分かりやすかったので、ここまでの知識で自分で設定したテーマに沿って実装してみたいと思います。

将棋盤と駒の検出構想

画像認識やディープラーニングの初学者ながら考えました。まず、以下のように段階を踏む必要があると思いました。

  1. 将棋盤のマスの位置(9x9マスそれぞれの位置)を検出
  2. 各マスの中の駒の判定
  3. 持ち駒の判定(余力あれば)

カメラに9x9のマスを作ってそこに当てはまるように撮る、というサービス仕様にするともっと楽になりますが、それだとあまり勉強にならないのでマス目から実装してみたいと思います。

将棋盤のマス目を抽出する

将棋盤のマス(黒い罫線)は4章の輪郭抽出が適してそうです。

とりあえず、本章のサンプルコードに何もフィルターをかけずに実行してみると何やら色んなオブジェクトの輪郭が抽出されました。

もちろんこれだけでは不十分で、マス目のみ抽出する必要があります。

まず、将棋盤の特徴は画像の中で最も大きい枠が9x9マスの外枠なのでは、と考えました。本に記載のコードに倣ってフィルターをかけますが、元画像のサイズを元に将棋盤の面積を考慮する必要があります。

以下のように元画像の面積の80%以上95%以下の輪郭のみを抽出するようにしました。

src_img = cv2.imread("hoge.jpg", cv2.COLOR_BGR2GRAY)
height, width = src_img.shape[:2]
contours, hierarchy = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

def allow_board_range(x):
return cv2.contourArea(x) > (width * height * 0.8) and cv2.contourArea(x) < (width * height * 0.95)

contours = list(filter(allow_board_range, contours))
print(len(contours)) # 1

 

上記でひとまず外枠の検出ができました。

さらにここから盤の中のマスの位置を取得します。盤の外枠の位置は先ほど分かったため、元の画像から外枠の領域をトリミングしてから先ほどのfindContoursを実行すればいけそうです。

先ほどのコードと被る所が多くて冗長なので省略しますがこのようになります。

 

マスだけでなくマスの中の駒も検出されてしまっています。ここでも将棋盤の特徴を利用します。9マスなので将棋盤の横幅と縦幅をそれぞれ9で割った数値でフィルターをかけます。罫線の幅も計算に含めることをお忘れなく。

 

うーん、想定では全てのマスが白線になるはずが30個ほど漏れています。フィルター条件の数値を0.9倍から0.7倍くらいまで下げると81個全て抽出されるのですが、誤差にしては少し大きい気がします🤔

contourAreaを使って面積で判定してるので誤差が起きやすいのと、他にも計算ミスがあるのかもしれません。

ハマりそうな罫線の太さの考慮は入ってるはずですが。。あと将棋盤は正方形ではなくて、マスの縦の方がやや長いので正方形だと思って同じ値を2乗すると誤差が出るので注意ですね。

今日はここまでかな_:(´ཀ`」 ∠):