【Xamarin.Forms】数独解くやつにUIつけてみたやつにOCR実装してみた

前回までのラブライお話は以下から。

やりたかったこと

せっかくマルチプラットフォームで作ってるんだし、モバイルデバイスのハードウェアとかも活用してみたいよね、という思いから、「問題をカメラで撮って問題を自動で認識して入力できたらカッコよくね?」とか考えついてしまったのが運の尽きでした。

Demo

とりあえずUWPで。そしてとりあえず画像ファイルを読み込む形式までにしてます。カメラは余裕があれば作っときます。

作ったもの

アプリケーション

Azure Functions

制作過程

意外と色々ハマったので順にお話しさせていただこうかなと。

数字の検出

まず安直に盤面の数字をお気楽極楽に検出したく、Azure Cognitive ServicesのComputer Visionという機能を検証してみました。

上のチュートリアルに従って、インプットしたファイルがこれ↓

で、戻ってきた検出結果を画像に反映したのがこれ↓

意外といけんじゃね?っていう。

しかし、そうは問屋が卸しませんでした。

何が問題だったかというと、画像にプロットしてヒトが目で見る分にはこれでよいのですが、プログラムで解釈する際にはこれが二次元配列になっていなくてはならないのです。

要は、「どのマスにどの数字が入るのか」は、これだけでは情報として不十分だったわけです。

検出された数字とその座標に加え、各マスも検出しなければならないことが分かりました。ということで次。

マスの検出

こちらについても最初はAzureの機械学習系の何かを使って実現できないかというのを主軸に考えてました。

案1)Azure Cognitive Servicesの既存モデルを活用

「数独のマスを検出するAPI」なんてニッチなものが提供されているわけもなく、却下。

案2)Custom Visionに独自モデルをデプロイ

Azure Cognitive Servicesには自分でモデルを作ってそれで推論とかできる機能もあったりするそうなのですが、こっちはこっちで学習――もっといえば学習データ採取――を自前でやらなければならず、あまりにもインテリジェンスではないということで却下。

案3)画像解析ライブラリで交点検出

以前、Udemyか何かでPythonによる画像処理の講義を見ていたときにOpenCVの話があったことを思い出し、「ただフレームを検出するなんて簡単なこと、むしろ機械学習なんか使わなくても方法論が確立されているのでは」と思い至り色々調べました。

結果、3つの更なる選択肢が浮かび上がってきました。

  1. Harrisのコーナー検出
  2. Shi-Tomasiのコーナー検出
  3. Cannyのエッジ検出

この中だとCannyが色々試していて精度が群を抜いて良さげだったので、最終的に案3-3を採用する運びと相成りました。

ちなみにCannyでエッジ検出するとこんな感じになります(Pythonで試した)。

実装上のはなし

で、こっからどうフレームを見つけるんだって話ですが、ググって出てきた以下のBAを参照させていただきました。というか細かいパラメータ以外まんまパクリです。

次に「Pythonでは実装できたけど、Xamarinでどうしよう」が問題となりました。ちなみにWindows環境かつスクリプト系以外の言語でOpenCVをアプリケーションに組み込む手段は現状ほぼC++一択です(.NET向けのラッパーがないわけじゃないけど)。

Microsoft公式ドキュメントを漁るとUWPでネイティブを使う方法や……

もっと直接的にOpenCVの簡単な使い方まで公開されておりました。

リファレンスの貯蔵が英雄王並みに十分そうだし、いけるんちゃうかと期待してましたが、甘かった。またしても問題発生。

それはどういうものかというと、ネイティブの cv::Mat 型からマルチプラットフォーム配列の Platform::Collections::Vector 型に変換ができなかった(方法が見つけられなかった)ということです。

私にもっと理解力があってもっと深く調べられていたら何らかの手段はあったんだと思いますが、それまでに直面した壁の数々と cv::Mat の型の定義とにらめっこした末、心が折れました。つらたん。

で、もうどうしようもなくて、「しょうがない、フレーム検出だけPythonで実装しよう」となってAzure Functionsにデプロイしたのが「作ったもの」の2つ目に掲載させていただいたものになります。

枠内判定

フレームが9×9=81個検出できれば、あとはその頂点座標とComputer Visionで取ってきた数字の頂点座標を突合して、どのマスの枠内にどの数字が入っているかを識別すればロジックとしては完成します。

枠内判定には以下のサイトを参照しました。

こちらのサイトの、

ある2次元上の点が多角形の内部にあるかどうかを判定するには、判定点からX軸に水平な半直線を描き、 多角形の各線分との交点の個数が奇数ならば、内部の点と判断すればよい

という説明には「おーそうか、そうやって分かるのかー(小並感)」くらいの納得感はあったのですが、その直後のソースコードが一体全体どう関連しているのか、正直さっぱり読み解けませんでした。すみませぬ。。。

「多角形に対する点の内外判定」について、もっとアカデミックな解説をされているサイトなども見つつ、どうもベクトルの内積を出して何かしているようだ、というところまではボヤッと分かりました……が、私にはそこまででした orz

ともあれ、紆余曲折ありましたが、いったんの成果物としては仕上がったのかなと思います。

あとは未実装のカメラとUWP以外のプラットフォームの対応ですかね(たぶんiOSはビルドできないのでやらないやつ)。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です