画像の類似度を判定するためのライブラリ Libpuzzleを試してみる

Libpuzzleは画像の類似度を計算することができるPHPのライブラリ。extensionの形で導入するので、コンパイル等が必要。

以下のサイトを参考に
- libpuzzleを使ってみる
- phpでlibpuzzleを使ってみる【備忘】
- how to install the libpuzzle extension for PHP

インストールする

基本的に、GDで動くのでその環境とlibpuzzleをコンパイルするためのGCCをインストールしておく

予め必要な物を揃えておく

$ yum install gd-devel
$ yum install gcc-c++
$ yum install gd

Libpuzzleをダウンロード

$ cd /usr/local/src
$ wget http://download.pureftpd.org/pub/pure-ftpd/misc/libpuzzle/releases/libpuzzle-0.11.tar.bz2
$ tar -jxvf libpuzzle-0.11.tar.bz2
$ cd libpuzzle-0.11

Libpuzzleをコンパイル

$ ./configure --with-libpuzzle
$ make
$ make install
$ cd php/libpuzzle/
$ phpize
$ ./configure --with-libpuzzle
$ make clean
$ make
$ make install

LibpuzzleをPHPでロードする

$ vi /etc/php.d/libpuzzle.ini

extension=libpuzzle.so

$ /etc/rc.d/init.d/httpd restart

サンプルを動かしてみる

サンプルはLibpuzzle with PHPから拝借

<?php

$file_a = 'images/01.jpg';  
$file_b = 'images/01_same.jpg';

// 画像のファイルを指定してシグネチャを生成する  
$cvec1 = puzzle_fill_cvec_from_file($file_a);  
$cvec2 = puzzle_fill_cvec_from_file($file_b);

// シグネチャを圧縮しておく  
$sign_1 = puzzle_compress_cvec($sign_1);  
$sign_2 = puzzle_compress_cvec($sign_2);

// シグネチャ同士を比較して画像の類似を判定する  
//  返り値は0.0~1.0の範囲で返ってき、少ないほど類似していることを表す  
$d = puzzle_vector_normalized_distance(puzzle_uncompress_cvec($sign_1), puzzle_uncompress_cvec($sign_2));

# Are pictures similar?  
if ($d < PUZZLE_CVEC_SIMILARITY_LOWER_THRESHOLD) {  
  echo "Pictures are looking similar\n";  
} else {  
  echo "Pictures are different, distance=$d\n";  
}

使ってみた感想

はっきり言えば人間的な「似ている」という判定とは程遠いような気がする。画像の下処理なんかをほとんどしていないので、もう少し工夫の余地があるのかもしれないが、少なくともデフォルトでは「似ている」感はない。

これは2つの意味で、似ていると思う画像が上がってこない場合と、似ていると言われても似ているように見えない場合。それぞれあるのでなんともいえない。ただ、目を薄っすらと開けて気持ちを穏やかに見てみたら、そこはかとなく似ているような気がすることもあるような気もする。

マニュアルを見てみると、デフォルトでは画像を9x9の賽の目に分割しているらしい。ならこれを増減させればと思ってpuzzle_set_lambdas()で賽の目数を変更しようとすると何故かpuzzle_compress_cvec()が動かなくなる。ローカルで動かしてみるとなんかエラーで落ちてるっぽい。

$ time php update2.php

BUG File: [compress.c] Line: [59]
アボートしました

少し試してみた結果、puzzle_set_lambdas()の値を少なくすれば、なんとなく似ている判定の画像が増えるような気がする。ただ同時にこりゃぁ似てねぇだろみたいなものも増えるのでなんとも言えない気持ちになってくる。いじるだけ損する。

あとは事前に画像の加工なんかすれば精度は挙がるのかもしれないけど、どう加工したら良いのかあんまりアイディアはない。なんとなく画像を400x300くらいまで極端に小さくしたら精度が上がるんじゃないかとも思うけど、実際どうなんだろうか。謎

補足事項

LIBPUZZLE - PHP EXTENSIONを参考に

The PHP extension provides bindings for the following tuning functions:
- puzzle_set_max_width()
- puzzle_set_max_height()
- puzzle_set_lambdas()
- puzzle_set_noise_cutoff()
- puzzle_set_p_ratio()
- puzzle_set_contrast_barrier_for_cropping()
- puzzle_set_max_cropping_ratio()
- puzzle_set_autocrop()

Have a look at the puzzle_set man page for more info about those.

Getting the signature of a picture is as simple as:

$signature = puzzle_fill_cvec_from_file($filename);

In order to compute the similarity between two pictures using their
signatures, use:

$d = puzzle_vector_normalized_distance($signature1, $signature2);

The result is between 0.0 and 1.0, with 0.6 being a good threshold to detect
visually similar pictures.

The PUZZLE_CVEC_SIMILARITY_THRESHOLD, PUZZLE_CVEC_SIMILARITY_HIGH_THRESHOLD,
PUZZLE_CVEC_SIMILARITY_LOW_THRESHOLD and PUZZLE_CVEC_SIMILARITY_LOWER_THRESHOLD
constants can also be used to get common thresholds :

if ($d < PUZZLE_CVEC_SIMILARITY_THRESHOLD) {
echo "Pictures look similar\n";
}

Before storing a signature into a database, you can compress it in order to
save some storage space:

$compressed_signature = puzzle_compress_cvec($signature);

Before use, those compressed signatures must be uncompressed with:

$signature = puzzle_uncompress_cvec($compressed_signature);