あるお仕事で類似画像検索を行うことになりました。
こちらがとても参考になりました。
が、PHPじゃなかったので、PHPで書いてみました。
類似画像検索のために、
画像からカラーヒストグラム(64色)を作成して、
DB(MySQL)に登録することにしました。
まずは、関数を作っておいて
// オリジナル画像のRGBからヒストグラムのビン番号を計算
function rgb2bin($red, $green, $blue) {
$redNo = (int)($red / 64);
$greenNo = (int)($green / 64);
$blueNo = (int)($blue / 64);
return 16 * $redNo + 4 * $greenNo + $blueNo;
}
以下、メイン処理
// 画像ファイルを読込み&画像サイズ取得
$filepath = ‘img/hoge.jpg';
$img = imagecreatefromjpeg($filepath);
$width= imagesx($img);
$height= imagesy($img);
// カラーヒストグラムを生成
$histogram = array();
for ($i = 0; $i < 64; $i++) {
$histogram[$i] = 0;
}
for($y = 0; $y < $width; $y++){
for($x = 0; $x < $height; $x++){
$rgb = imagecolorat($img, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8 ) & 0xFF;
$b = $rgb & 0xFF;
$bin = rgb2bin($r, $g, $b);
$histogram[$bin] += 1;
}
}
imagedestroy($img);
// ヒストグラムを全体を1になるように正規化
for ($i = 0; $i < 64; $i++) {
$histogram[$i] = $histogram[$i] / ($width* $height);
}
これで $histogram 配列にヒストグラムが入ったので、
後は、それら 64 つの値を、そのままDBに登録するだけです。
(登録処理は省略)
DBに入ったら、やっと類似検索ができます。
以下のSQLでは、検索元となる画像のヒストグラムと
その画像も含めた全てのヒストグラムをクロスジョインして
類似度(Histogram Intersection)を計算し、
類似度の降順で結果を返しています。
select a.id as a_id, b.id as b_id,
truncate(LEAST(a.bin_00, b.bin_00) + LEAST(a.bin_01, b.bin_01) + LEAST(a.bin_02, b.bin_02) + LEAST(a.bin_03, b.bin_03) + LEAST(a.bin_04, b.bin_04) + LEAST(a.bin_05, b.bin_05) + LEAST(a.bin_06, b.bin_06) + LEAST(a.bin_07, b.bin_07) + LEAST(a.bin_08, b.bin_08) + LEAST(a.bin_09, b.bin_09) + LEAST(a.bin_10, b.bin_10) + LEAST(a.bin_11, b.bin_11) + LEAST(a.bin_12, b.bin_12) + LEAST(a.bin_13, b.bin_13) + LEAST(a.bin_14, b.bin_14) + LEAST(a.bin_15, b.bin_15) + LEAST(a.bin_16, b.bin_16) + LEAST(a.bin_17, b.bin_17) + LEAST(a.bin_18, b.bin_18) + LEAST(a.bin_19, b.bin_19) + LEAST(a.bin_20, b.bin_20) + LEAST(a.bin_21, b.bin_21) + LEAST(a.bin_22, b.bin_22) + LEAST(a.bin_23, b.bin_23) + LEAST(a.bin_24, b.bin_24) + LEAST(a.bin_25, b.bin_25) + LEAST(a.bin_26, b.bin_26) + LEAST(a.bin_27, b.bin_27) + LEAST(a.bin_28, b.bin_28) + LEAST(a.bin_29, b.bin_29) + LEAST(a.bin_30, b.bin_30) + LEAST(a.bin_31, b.bin_31) + LEAST(a.bin_32, b.bin_32) + LEAST(a.bin_33, b.bin_33) + LEAST(a.bin_34, b.bin_34) + LEAST(a.bin_35, b.bin_35) + LEAST(a.bin_36, b.bin_36) + LEAST(a.bin_37, b.bin_37) + LEAST(a.bin_38, b.bin_38) + LEAST(a.bin_39, b.bin_39) + LEAST(a.bin_40, b.bin_40) + LEAST(a.bin_41, b.bin_41) + LEAST(a.bin_42, b.bin_42) + LEAST(a.bin_43, b.bin_43) + LEAST(a.bin_44, b.bin_44) + LEAST(a.bin_45, b.bin_45) + LEAST(a.bin_46, b.bin_46) + LEAST(a.bin_47, b.bin_47) + LEAST(a.bin_48, b.bin_48) + LEAST(a.bin_49, b.bin_49) + LEAST(a.bin_50, b.bin_50) + LEAST(a.bin_51, b.bin_51) + LEAST(a.bin_52, b.bin_52) + LEAST(a.bin_53, b.bin_53) + LEAST(a.bin_54, b.bin_54) + LEAST(a.bin_55, b.bin_55) + LEAST(a.bin_56, b.bin_56) + LEAST(a.bin_57, b.bin_57) + LEAST(a.bin_58, b.bin_58) + LEAST(a.bin_59, b.bin_59) + LEAST(a.bin_60, b.bin_60) + LEAST(a.bin_61, b.bin_61) + LEAST(a.bin_62, b.bin_62) + LEAST(a.bin_63, b.bin_63) + 0.005, 2) as intersection
from color_histogram as a
cross join color_histogram as b
where (a.id = ‘hogehoge’) order by intersection desc;
同じ画像の場合は類似度は1になります。
1に近いほど似ていて、0に近いほど似ていません。
このSQLで類似度が出せるのは、
事前にヒストグラムの合計値が1になるように正規化しているためです。
まだまだ、試作段階で、
件数が多くなったときの速度が非常に心配ですが、
今のところはこんな感じです。