🔢

GoCVを使って画像からナンプレを解いてみた

2024/02/13に公開

したこと

タイトルの通り、GoCVを使って、ナンプレの自動読み取りをしてみました。

n+1番煎じですが、昔からの夢だったので作ってみることにしました。Pythonの記事はいくつかあったのですが、そのまま写すだけだと少しつまらないので、せっかくなのでGoで書くことにしました。

完成品

環境

M1 Mac
OS: macOS Sonoma version 14.2.1

$ brew --version                    
Homebrew 4.2.7-140-g352f57d
Homebrew/homebrew-core (git revision 26150f0a753; last commit 2024-02-12)

セットアップ

基本的に公式のGetting Startedの通りにすれば問題ないと思います。

https://gocv.io/getting-started/macos/

brew install opencvにかなり時間がかかったので、初めて入れる人は時間に余裕がある時にすることをおすすめします。

実装

コードの全文を載せておきます。

https://github.com/hamao0820/sudoku

OCR用の学習コードはこちらです。

https://github.com/hamao0820/sudoku-ocr-torch

大まかなステップは以下の4つです。主に、GoCVを使うのは1と2です

  1. 正方形の検知
  2. マス目ごとの写真を取得
  3. マス目の値を読み取る
  4. ナンプレを解かせる

正方形の検知

実装は基本的に以下の記事のコードをGoCVで書き直しました。

https://qiita.com/hatt_takumi/items/47a46d5e85223a41afa4

この記事で紹介されている、以下のリポジトリを参考にしました。

https://github.com/na-o-ys/shogi-camera

すこし難しかったので、後半の方は省略しています。ですので、元のコードより精度が少し落ちているかもしれません。

基本的な流れは以下の通りです。正直、深いところまではわかっていないため、間違いがあるかもしれないです。

  1. 画像をグレースケール化する
  2. Cannyアルゴリズムを使ってエッジ検出をする
  3. 輪郭(をなす頂点の集合)を検出する
  4. 輪郭の内部の面積が小さいものを除外する
  5. それぞれの輪郭の凸包を取得
  6. それらの中で、最も正方形に近いものを選択
  7. 透視投影変換を行なって真上みた図に変形する

実際の実装は以下のコードを見てください。

detect/trim_boad.go

苦労した点としては、PythonのCV2と型や関数の引数が違うことです。Pythonのcv2のドキュメントより、C++のドキュメントを読んだ方がわかりやすかったです。

注意点としては、cv2やC++の同じ名前の関数で、いくつかの引数が省略されているものがありました。C++と同じ実装をしたい場合は、(同じ関数目) + WithParamsという関数を用いる必要があります。詳しくはGoCVのドキュメントでご確認ください。

https://pkg.go.dev/gocv.io/x/gocv@v0.35.0

マス目ごとの写真を取得

正方形の写真が取得できたら、マス目はただ正方形を9x9に分割するだけです。
一応、マスの境界線を取り除くため、4方1pxずつ内側を取るようにしましたが、効果があったのかは不明です。

マス目の値を読み取る

学習はこちらのリポジトリで行っています。ここで学習した.ptファイルをコピーして移動させて推論に使っています。

https://github.com/hamao0820/sudoku-ocr-torch

Tesseractやそのラッパーであるgosseractを使ってみたのですが、うまく読み取れなかったので、自分でNNを実装することにしました。

本当はすべてGoで完結したかったのですが、Goの機械学習ライブラリでは大変だったので、素直にpytorchで実装しました。

モデルは畳み込み層2層と全結合層2層のシンプルな構造ですが、かなり精度良く認識できていました。

model.py

以下の記事を参考にしました。

https://qiita.com/a5chin/items/6d35283a54a1022f9b24

この記事で紹介されているtimmCosineLRSchedulerという学習率のスケジューラを使用しましたが、かなりよかったです。

また、この記事同様、空白の部分を間違えることが多かったので、この記事のように線をランダムでいれるデータ拡張をしてみたところ、間違えることがほとんどなくなりました。

先ほども言った通り、Goで完結させたかったので、推論部分はGoで動かすつもりでした。しかし、PyTorchのGoラッパーであるgo-torchgotchTensorflow in Goが動かなかったので、裏でPythonのAPIサーバーを立てて、APIで画像を送って認識結果を受け取るというすこし不細工な実装になりました。

もしM1 MacでPyTorchかTensorflowを動かす方法がわかる方がいましたら教えてください。

ナンプレを解かせる

ナンプレはシンプルなバックトラック法で解きました。
詳しくはコードをご覧ください。

まとめ

思ったよりOpenCVだけでできることが高機能で驚きました。参考コードの省いた部分を実装して、もう少し精度を上げたいです。

PyTorchはM1 MacでもGPUが使えるのがありがいですね。いつかはWindowsでつよつよGPUで学習をぶん回してみたいです。
timmに出会うことが出来たのが思わぬ収穫でした。

GoでPyTorchやTensorflowレベルの機械学習ライブラリができて欲しいですね...
あるいは、GoのTensorflowがTensorflow.jsくらい簡単に導入できるようになれば良いのですが。

推論部分だけで良いので、M1 MacでPyTorchかTensorflowの導入方法がわかる方がいましたら、コメントで教えていただきたいです。

参考記事

https://analytics.livesense.co.jp/entry/2017/12/21/090000

https://qiita.com/hatt_takumi/items/a44916d2ff86ab8d7105

https://qiita.com/a5chin/items/6d35283a54a1022f9b24

https://mattn.kaoriya.net/software/lang/go/20180825013735.htm

Discussion