Kasasagi’s memorandum

JavaとかProcessingとか。最近はAtcoderとか。

サイゼリヤの間違い探しを画像処理で解いてみた

この記事は高専 Advent Calendar 2018 - Adventar の 5日目の記事です。

昨日はnamachan10777さんの「キーボードを作る話し」でした。
OpenSCAD with clojure - エリマキトカゲになりたい

関数型言語使いこなせててすごい。。。私は???って感じです。

こんにちは、かささぎといいます。高専Advent Calendarを書かせていただきます。

自己紹介

・学校    石川工業高等専門学校
・学年,学科 電子情報工学科 5年
Twitter   @hrt9092

 宗教
 ・OS      Windows10
 ・エディタ   SublimeText3
 ・言語     Java,Processing,Python,Rubyあたり
 ・IDE      Intellij Idea
 ・きのたけ   たけのこ
 ・唐揚げレモン かける派

#procon27 ~ #procon29に参加し、人力でパズルを並べたり、人力でパズルを並べたり、競技アイデア募集企画ってので優秀賞をいただいたりしてました。

本題

私は画像処理系の研究室にいるのですが、自分の研究では本格的な画像操作を行っておらず、なにかやりたいなーと思ってました。

そこでAdvent Calendar に合わせてなにか作ってWrite UP しようと思い、間違い探しを解かせてみることにしました。
間違い探しは、サイゼリヤのキッズメニューについてくるものを利用させていただきました。こちらのHPに問題があります→エンターテイメント|サイゼリヤ

サイゼリヤの間違い探しを画像処理で解くという試みには、先行研究(研究か?)がありました。こちらの方の記事を参考にさせて頂きました→河本の実験室: サイゼリヤの間違い探しが難しすぎたので大人の力で解決した

OpenCVが使えれば世の中の大抵の問題は解決できる。

格好いいですね。

環境

・OS     Windows10
・言語    Python 3.6.5rc1
・ライブラリ opencv-contrib-python 3.4.2.16

本当はRubyでやってみたかった(Pythonでは一応経験があったため)のですが、Windows+Ruby+OpenCVの相性がクソゴミだったので、断念しました。

開発中にOpenCV4がリリースされたのですが、よくわからなかったのでアップデートしてません。

また後述のSIFTアルゴリズムを用いるため、拡張モジュールを含むcontribを入れました。
よくわかりませんが、商用利用が面倒くさいみたいです。

解説

プロジェクトはGitHubに置きました→https://github.com/hiroto3432/matigaisagasi
main.pyが実行ファイルで、solver.pyのクラスやメソッドを呼び出します。

ソースコードは載せるととても長くなるのでここには載せません。GitHubのコードを見てください。

解説にはこの画像を利用します。
f:id:yh9092:20181125113413p:plain

画像の分割

ただ単純に半分で分割すればいいと思っていたのですが、余白のサイズや、左右それぞれの画像のx座標の開始位置が問題画像によってバラバラだったので、自動で検出させることにしました。

手法としては、

  1. とりあえず半分で分割
  2. 分割した2枚の画像から、SIFTアルゴリズムで特徴点マッチング
  3. マッチング結果から、分割すべき画像の横の長さと余白の大きさを求めトリミング

という感じです。

特徴点マッチングとは、2枚の画像のこことここが同じだよーというところを検出することです。
これには、SIFT,SURF,AKAZEといったアルゴリズムOpenCVに用意されています。
こちらを参考にさせて頂きました→OpenCV(Python)で物体認識、特徴抽出(SIFT、SURF、A-KAZEの使い方)

自分で試してみた結果、AKAZEよりSIFTのほうが精度が高い(SURFはよくわからないけど処理が終わらずPCが悲鳴を上げた)ということで、SIFTを利用します。

マッチング結果から、y座標が同じものを描画したものがこちらになります。
f:id:yh9092:20181125112358p:plain

マッチングした点の左の画像と右の画像それぞれのx座標の差分から、余白のサイズと画像の横の長さを求めることができます。

実際はすべてのマッチング結果が同じ値を示すことはないので、結果を辞書に格納していって多数決で求めました。

分割結果はこんな感じです。

f:id:yh9092:20181125113123p:plainf:id:yh9092:20181125113137p:plain

背景差分

次に背景差分というのを取ります→背景差分 — OpenCV-Python Tutorials 1 documentation

cv2.bgsegm.createBackgroundSubtractorMOG() というので簡単にできます。しかしめちゃくちゃ長い名前のメソッドですね。

差分画像はこのようになります。
f:id:yh9092:20181125113859p:plain:h400

白いところが間違いになってますね。

オブジェクト検出

次に差分画像から、オブジェクト検出で差分の領域を取得します。
cv2.findContours()メソッドで簡単に取得できますが、この取得して得られたものをすべて描画するとこのようになります。
f:id:yh9092:20181125114654p:plain:h400

大きな領域の中に小さい領域がたくさん含まれてしまいます。
これを防ぐために、他の領域に包含される領域は描画しないようにしました。

結果

このようになりました。
f:id:yh9092:20181125115015p:plain

ひとつの間違いが複数の領域に分割されているのもありますが、なかなかよい結果になったのではないでしょうか。

ちなみに検出できたのは9つで一つ足りません。これは左上の”Kids menu"のiの一画目が丸いか四角いかという細かな違いなのですが、ノイズとして除外してしまったようです。

ついでにもう一つの画像の結果です。

元画像
f:id:yh9092:20181125115758p:plain
検出結果
f:id:yh9092:20181125115822p:plain

ところどころ誤検出されてますね…。ノイズとの兼ね合いが難しいです。

まとめ

画像処理では間違っている部分の表示は簡単でしたが、その領域を綺麗に検出することは難しかったです。

OpenCVの様々な技術を用いたので、とても勉強になりました。

OpenCVはほかにも、3次元復元や動画解析、機械学習などができ、とても楽しいです。
Pythonの他にもC++Java等でも利用できるので、ぜひ試してみてください。

みんなで堕ちよう画像処理沼!


明日6日目はsei0oさんの「たのしい潮寮生活 2」です。
高専 Advent Calendar 2018 - Adventar
私は通生なので詳しくありませんが、寮生がゴミ回線と虚無な飯と点呼によって精神を病むのは全国共通なんですかね?うちがひどいだけ?


それでは。


p.s.
12/22の高専カンファレンス in 名古屋 2018に参加するので、仲良くしてください。