画像認識に用いられるライブラリといえば OpenCV が一般的ですが、毎回 face detect のデモを動かしてみて安心してしまい、結果的にいつも本腰を入れて使ってみることがありませんでした。(やりたいなぁとは思いつつ、特に目的がない…)
今回、知人と OpenCV の話題が出たのをきっかけに ruby-opencv を使ってみようとしたけど挫折。OpenCV 本体のソースコードを clone してきて 3 時間近くもビルドを走らせて、環境作りにえらく時間がかかっちゃいました。もう少し根気があればいいんですが…。
さて、高機能な画像認識は必要がないような例、例えば、かなりゆるめの画像の同一性チェックなどでは、OpenCV のような本格的なライブラリを用意しなくても既存の環境でなんとかしたくなってしまいます。おっと、挫折したからって逃げではないですよ!ほんとですよ!
ということで、みんな大好き ImageMagick の convert コマンドに丸投げする形で、Ruby で書いていきたいと思います。 こちらの記事によると、縮小した画像を様々な視点からハッシュ化して、その値を比較することで類似度を算出しています。
今回は単純に明度同士の比較用いて、類似度を計算したいと思います。
手順
- ImageMagick を使って、比較対象の画像をそれぞれ 16×16 ピクセルの小さな画像に変換。言うなれば、モザイクのように色の平均値のパッチの集合となる。
- それぞれを2値化して、XBM で保存。XBM で保存するのは、Ruby 側から大掛かりな画像ライブラリを介すことなく、簡単に各々のピクセルの色情報を取得するため。
- 各々の XBM に含まれるピクセルデータをビット列に変換。
- 2つのビット列を比較し、類似性スコアを算出。
こういうのを「ハミング距離」を用いた判定と言うらしいです。
スクリプト
入力画像
試しに、次の2つの画像を入力として与えます。256×256 と 512×512 の大きさの違うレナさんです。
2値化した XBM として出力されるのは、それぞれ次のとおり。
似ていますが、入力のソースが若干違うために2値化結果にも差異が出ています。
この画像をビット列に変換し、スコア値を印字すると 0.9375
と表示されました。
面白い傾向として、最初に小さな画像にサンプリングする際の解像度を上げれば、より精度の高い類似性検出ができるのかと思いがちですが、実際にはその逆のパターンが多く見受けられました。
出力されるスコア値は 0.0 から 1.0 の値を取り、1.0 に近くなればなるほど類似度が高くなります。また、0.0 に近くなればなるほど、比較対象の明度が元画像に対して反転している可能性が高くなります。
この例では明度を2値化しているので、例えばほとんどのピクセルの明度が閾値以下(または、以上)になるような画像同士の比較には向いていません。また、部分マッチやトランスフォーメーション(回転・移動)に対する類似性を検出もできません。高度な検出を可能にするならば、やはり OpenCV を使うべきですが、明度だけではなく彩度やレベル・トーンカーブなど色々な側面を数列化して比較するとコストをかけずに検出精度を上げることも可能だと思われます。