PHPでカラーヒストグラム

あるお仕事で類似画像検索を行うことになりました。

こちらがとても参考になりました。

が、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になるように正規化しているためです。

 

まだまだ、試作段階で、

件数が多くなったときの速度が非常に心配ですが、

今のところはこんな感じです。