OpenCV (ruby-opencv) を用いて「ある画像が画像のどこに埋め込まれているか」を調べる(「坊ちゃんを探せ!」)

動機

「坊ちゃんを探せ!」と、あとまさんがツイートをしたので探してみようと思いました。

方針

  1. 元画像ファイルを保存する
  2. 元画像ファイルから探すべき画像を切り出してファイルとして保存する
  3. OpenCV の「テンプレートマッチング」を用いて、切り出した画像が元画像とマッチングするかを調べるコードを書く
  4. マッチングした座標(元画像)を出力する

実装

前述の方針に従ってひとつひとつ進めていきます。

1. 元画像ファイルを保存する

前掲のあとまさんのツイートの添付画像を保存します。表示されている画像を右クリックで保存するのではなく、画像のユニーク URI に:origオプションを付与してオリジナルの画像を保存します(https://pbs.twimg.com/media/Cw6NGAtUcAAKvyJ.jpg:orig)。

2. 元画像から探すべき画像を切り出してファイルとして保存する

元画像の右下に探すべき画像がありますので、そこの画像を切り出します。切り出す範囲に悩むところですが、「黒」の部分が含まれていると誤判別しやすくなるので、黒色の縁を含まない最大の切り出し範囲を設定しました(96x192)。これでもし判別できないようならば範囲を広げてやり直していきます。

f:id:gregminster:20170701202347p:plain

なお、切り出したままの画像は 1ドット が 16x16 ですが、マッチングをさせたい画像は 1ドット が 8x8 ですので、縮小をしておくことを忘れないようにします。

f:id:gregminster:20170701203314p:plain

3. OpenCV を用いてマッチングを調べる

OpenCVの実装にはRubygemであるruby-opencvを用います。

gemをインストールするためにはもちろんOpenCVがインストールされている必要があるので、Ubuntu ならばapt-get install libopencv-devあたりで入れておきます*1

テンプレートマッチングのコードは、ruby-opencvのコミッタである ser1zw さんがそのものズバリのサンプルコードを示してくれていますので、これをベースに書いていきましょう。

「探すべき画像(切り抜いた画像)」をsearch_target.pngとし、元画像をoriginal.jpgという名前にすると、以下のようなコードになります。

require 'opencv'
include OpenCV

target_image   = CvMat.load('search_target.png')
original_image = CvMat.load('original.jpg')

matching_result = original_image.match_template(target_image, :sqdiff)

puts "マッチング画像の元画像での座標の始点は次のとおりです"
puts "x: #{matching_result.min_max_loc[2].x}, y: #{matching_result.min_max_loc[2].y}"

4. マッチングした座標(元画像)を出力する

あとは実行するだけです。上掲のコードをsearch_bocchan.rbとして実行します。ついでに実行時間も計ります。

$ time ruby search_bocchan.rb                                                        
マッチング画像の元画像での座標の始点は次のとおりです
x: 378, y: 218

real    0m0.783s
user    0m0.656s
sys     0m0.084s

無事、座標が得られたようです。当該座標は元画像の左上のあたりに位置していて、確かめてみると目視では確かに一致しているように見えます*2

かかった時間は 0.78秒 でした。

備考

こういうことをすると確実にモテないのでやめたほうがいいと思います*3

*1:えらい量のパッケージが入ります

*2:実際にあとまさんに尋ねて正解であることを確認済みです

*3:それか言わないほうがいいと思います

Powered by はてなブログ