Node.jsで画像比較が行えるようになるnode-phashを使ってみる
node-libpuzzleの検証に続き、画像比較ライブラリの検証です。
まず、pHashとはなんぞ?
Hashというのは同じデーターには同じ値/違うデータには違う値、 という分類をデータサイズを抑えた形で一意に表す仕組みなのですが、 pHashはさらにHash同士を比較して類似度を算出できるようにしたものです。
これを使うと、 例えば画像ファイルからpHashを計算しておけば、pHash同士を比較するだけで似ている画像ファイルを探すといったことができるわけです。
要はすごいHashなわけですよ。(テキトウ)
ちなみにPerceptual Hash
の略です。
先に依存ライブラリを入れる
依存しているのは以下の2つ。
CImgをインストール
$ wget http://sourceforge.net/projects/cimg/files/CImg-1.5.7.zip/download
$ unzip CImg-1.5.7.zip
$ sudo cp CImg-1.5.7/CImg.h /usr/local/include/
一瞬ビルドしないの?って思ったけど、CImgってテンプレートライブラリなんですね。
pHashをインストール
pHashライブラリは画像/音声/動画からpHash値を求めることができるようですが、画像だけ使えればいいので音声と動画は無効化します。
$ wget http://www.phash.org/releases/pHash-0.9.6.tar.gz
$ tar xzf pHash-0.9.6.tar.gz
$ cd pHash-0.9.6
$ ./configure --enable-video-hash=no --enable-audio-hash=no
ここまではうまくいった。が、
$ make
...
../src/.libs/libpHash.so: undefined reference to `pthread_create'
../src/.libs/libpHash.so: undefined reference to `pthread_join'
collect2: ld returned 1 exit status
make[2]: *** [test_texthash] Error 1
make[2]: Leaving directory `/home/admin/tmp/pHash-0.9.6/examples'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/home/admin/tmp/pHash-0.9.6'
make: *** [all] Error 2
makeでエラーが出た。
pthread
というのはPOSIXスレッドと呼ばれるものらしいが、全く知らない分野なので分からない。
ここから、makeファイルをいじったりしてtry&errorで色々試したけど、解決できなかった。
で、発想を変えて対応
pthreadsは比較的新しい仕組みということがどこかのサイトに書いてあり、pthreadsが新しい仕組みであるならば古い仕組みのスレッド実装方法があるはずであり、configure
で指定できるのではないかと思って調べてみました。
$ ./configure --help
....
--disable-pthread pthread support [default=no]
あった。
これで勝つる!
$ ./configure --enable-video-hash=no --enable-audio-hash=no --disable-pthread
$ make
$ sudo make install
pthreadsを使わないとパフォーマンスには劣るのかもしれないけど、無事インストールできました。
node-phashをインストール
$ npm install phash
これはすんなり入った。
使ってみる
ソース
var pHash = require('phash');
var file1 = process.argv[2];
var file2 = process.argv[3];
console.log('compareFile: ', file1, file2);
var startTime = new Date().getTime();
var hash1 = pHash.imageHashSync(file1);
var hash2 = pHash.imageHashSync(file2);
var calcTime = new Date().getTime() - startTime;
console.log('calcTime:', calcTime + 'ms');
var distance = pHash.hammingDistance(hash1, hash2);
console.log('distance:', distance);
var compareTime = new Date().getTime() - startTime - calcTime;
console.log('compareTime:', compareTime + 'ms');
実行結果
compareFile: img_01_l.jpg img_02_l.jpg
calcTime: 6845ms
distance: 28
compareTime: 1ms
1MB近くあるファイル同士を比較したのでHash作成には時間がかかっていますが、比較は早いですね。 また、node-libpuzzleとは違い、計算したHash値をDBに格納できるので使いやすそうです。
pHash.hammingDistance()
の結果は、0が全く同じで数字が離れるほど違うというものだそうです。
不満点
pHashのHash値は64bitなのですが、この精度だと二次絵の「いわゆる差分画像」の違いを吸収してしまうため、表情が違う絵などが同じ絵だと認識されてしまうようです。あと、漫画のようなモノクロ絵も精度が少し悪いです。これはBit数を増やせれば精度が上がりそうな気がするのですが、そういうパラメータは無いようです(というか呼び出しているpHashの実装が64bit固定みたい)。
写真であれば使えると思うのですが、イラスト系だとこれに頼って実装するかというと、ちょっと迷う精度ですね。