Perl で作る画像類似検索システムの考察

今日はとてもショッキングな出来事がありました。あまりにショックがでかいので何かに没頭しなければ気が紛れそうにありません。と言うわけで全く専門分野でもないし当面使う予定もないのですが、1年ほど前にちょっと気になっていた画像の類似検索についていろいろ調べてみました。

どうやら ImgSeek ってソフトが結構有名らしいです。最新バージョンは 0.86 で Linux Only です。1つ前のバージョン 0.85 は Windows binary があります。

img02.jpg

- スポンサーリンク -

過去にいくつか画像類似検索ソフトを試したような記憶がありますが忘れてしまいました(vector でも結構類似検索ソフトありますね)。まずは windows binary 版をダウンロードしてきて実行してみました。

img01.jpg

それなりに使えそうな予感がします。Linux 向けの imgSeek-0.8.6.tar.bz2 をダウンロードしてインストールしてみました。どうやら Python で作られていて GUI ベースのアプリでした。サーバサイド向けに作り直したのが isk-daemon ってやつです。現在の最新バージョンは 0.6 なんですが、こちらも Python で作られていました。専門外なので Perl でないものかね?と思って探してみたら、Image::Seek ってモジュールがありました。python で書かれた imgseek を perl-xs にポーティングしたものです。

ImgSeek (http://www.imgseek.net/) is an implementation of Haar wavelet decomposition techniques to find similar pictures in a library. This module is port of the ImgSeek library to Perl's XS. It can deal with image objects produced by the Imager and Image::Imlib2 libraries.

と言うわけで、早速使ってみました。まずは Image::Seek インストールから。Imager か Image::Imlib2 が必要なのでインストールはこんな感じです。

cpan install Imager
cpan install Image::Imlib2
cpan install Image::Seek

なにやら Image::Imlib2 の方はインストールが失敗します。

# running Build.PL
/usr/local/bin/perl Build.PL
You must install the imlib2 library before you can install
Image::Imlib2. You can obtain imlib2 from
http://sourceforge.net/projects/enlightenment/

Alternatively, if you have downloaded and installed imlib2 and this
still will not work, modify the $CONFIG variable inside Build.PL to
point to the imlib2-config program that provides.
Can't read _build/prereqs: そのようなファイルやディレクトリはありません at /usr/local/lib/perl5/site_perl/5.8.7/Module/Build/Compat.pm line 244.
Running make test
CPAN: YAML loaded ok
  Make had some problems, won't test
Running make install
  Make had some problems, won't install

調べるのが面倒くさいので Imager + Image::Seek で良しとしておきます。インストールが終わったら類似検索用のデータベースを作成する必要があります。スクリプトが特に用意されているわけではないので適当にでっち上げる必要があります。ちなみに、類似検索 DB は整数値 id での管理になるので、id と画像ファイルのパスの対応を管理するのも自前でやる必要があります。

↓ でっちあげた makedb.pl スクリプト(右クリック→保存で保存できます。)

use strict;
use File::Find;
use Imager;
use Image::Seek qw(loaddb add_image query_id savedb);
use Storable qw/nfreeze thaw/;

my $folder   = $ARGV[0] || './';
my $namehash = {};
my $id       = 1;
my $namedb   = 'imgname.db';
my $imgdb    = 'imgseek.db';

if ( -e $namedb ) {
    open my $fh, '<', $namedb || die;
    my $fzdata = do { local $/; <$fh> };
    close $fh;
    $namehash = thaw($fzdata);
    $id = $namehash->{_maxid};
}

loaddb($imgdb);
my $img = Imager->new();

find( \&filter, $folder );

$namehash->{_maxid} = $id - 1;
savedb($imgdb);

open my $fh, '>', $namedb or die;
print $fh nfreeze($namehash);
close $fh;

sub filter {
    eval {
    if ( $File::Find::name =~ /jpg$/ && $File::Find::name !~ /thumb/ && !defined $namehash->{_name}->{$File::Find::name} ) {
        $img->open( file => $File::Find::name );
        add_image( $img, $id );
        $namehash->{_id}->{$id} = $File::Find::name;
        $namehash->{_name}->{$File::Find::name} = $id;
        print "[$id]$File::Find::name\n";
        $id++;
    }
    };
    if($@) { warn $@; }
}

このスクリプトは下記のように実行します。ディレクトリ名を省略すると './' で画像を探します。

 perl makedb.pl ディレクトリ名

Image::Seek モジュールはまだ ver 0.01 なので die して死ぬときがあります。eval してあげてスクリプトが不正終了しないようにしたりといろいろ面倒。ともかく正常終了すると、以下のファイルが生成されます。いちおうインクリメンタルに増分だけ DB 追加できると思います。多分・・・

 imgname.db : ID 管理 DB / imgseek.db : 類似検索 DB

さてこれで準備が整いました。あとはフロントの CGI を作れば類似画像検索が可能となります。例えばこんな感じ。ちょっと長いので記事としてのせるのは割愛。でっちあげた seek.cgi スクリプトは右クリック→保存で保存してください。utf8 なのでそのままブラウザで開くと文字化けしてるかも。
めっちゃ適当に作っているのでバグとかみつけても見逃して下さい。

さて実行画面はこんな感じ。

img03.jpg

当然ながら、元画像と一番似ているのは元画像なので、類似画像の一番上の画像=元画像となります。以下表示される画像が類似画像ということになります。サムネイル画像を検索対象としている場合は、元画像の直下に表示されます。ある意味当然。

いちおうここにオンラインデモをおいておきます。結構高速に動いてます。


さて気になる精度に関しては画像の種類にかなり左右される感じです。ハァ?って場合もままありますが、よく似た画像はちゃんとでているような気はします。自分のブログ等に手軽に関連する画像は・・・なんて風に表示できるのはなんだかイイんじゃない?なんて思ったり。

ポータルサービスの類似画像検索レベルには全然なってませんが、自前のちっこいサービスくらいには使えそうなので何かしらのサービス作ろうと思ってます。

- スポンサーリンク -