導入:なぜHSV色空間による比較が必要なのか
画像処理において「似ている画像」を検索したい場合、単純にRGB値の平均値を比較する方法があります。しかし、RGBはコンピュータが扱いやすい形式である一方、人間の知覚とは乖離しています。例えば、少し明るさが変わっただけでRGB値は大きく変化してしまいます。そこで、色相(Hue)、彩度(Saturation)、明度(Value)に分解して扱うHSV色空間を利用することで、人間が感じる「色の近さ」に近い精度での画像比較が可能になります。
基礎知識:HSVモデルとは
HSVモデルは以下の3つの要素で色を表現します。
・色相 (Hue): 赤、青、黄色といった色の種類。0~360度の範囲で表されます。
・彩度 (Saturation): 色の鮮やかさ。0~1の間で表されます。
・明度 (Value): 色の明るさ。0~1の間で表されます。
このモデルを使うことで、「色味は同じだが明るさが違う」といった比較を論理的に計算できるようになります。
実装:類似画像検索のロジック
今回の実装では、GDライブラリを使用して画像をリサイズ(サムネイル化)し、画素の平均色を算出してからHSVへ変換します。これにより、高解像度の画像でも高速に処理できます。
サンプルプログラム
以下のコードは、指定ディレクトリ内の画像群から、基準画像と色合いが近い順に並び替えて出力する実用的なサンプルです。
1, ‘s’ => 1, ‘v’ => 1];
// 画像をHSV情報に変換して取得
$sample_hsv = imageHsv(loadImage($filepath));
$diff = [];
foreach (glob($dir . “.{jpg,png,gif}”, GLOB_BRACE) as $file) {
$hsv = imageHsv(loadImage($file));
$name = basename($file);
// HSVの差分を計算
$diff[$name] = hsvDistance($sample_hsv, $hsv, $priority);
}
// 差分が小さい順(似ている順)にソート
asort($diff);
foreach ($diff as $filename => $score) {
echo ‘‘;
}
/
- GDライブラリで画像を読み込む
/
function loadImage($filepath) {
$info = getimagesize($filepath);
$type = $info[‘mime’];
if ($type == ‘image/jpeg’) return imagecreatefromjpeg($filepath);
if ($type == ‘image/png’) return imagecreatefrompng($filepath);
if ($type == ‘image/gif’) return imagecreatefromgif($filepath);
return null;
}
/
- 画像を縮小して平均色を取得しHSVに変換
/
function imageHsv($image) {
$thumb = imagecreatetruecolor(16, 16);
imagecopyresampled($thumb, $image, 0, 0, 0, 0, 16, 16, imagesx($image), imagesy($image));
$r = $g = $b = 0;
for ($x = 0; $x < 16; $x++) {
for ($y = 0; $y < 16; $y++) {
$rgb = imagecolorsforindex($thumb, imagecolorat($thumb, $x, $y));
$r += $rgb['red']; $g += $rgb['green']; $b += $rgb['blue'];
}
}
imagedestroy($thumb);
return rgb2hsv(['red' => $r/256, ‘green’ => $g/256, ‘blue’ => $b/256]);
}
/
- HSV距離の計算
/
function hsvDistance($hsv1, $hsv2, $priority) {
$dist_h = abs($hsv1[‘h’] – $hsv2[‘h’]);
// 色相は環状のため360度を考慮
$dist_h = min($dist_h, 360 – $dist_h);
return ($dist_h $priority[‘h’]) +
(abs($hsv1[‘s’] – $hsv2[‘s’]) 180 $priority[‘s’]) +
(abs($hsv1[‘v’] – $hsv2[‘v’]) 180 $priority[‘v’]);
}
/
- RGBからHSVへ変換
/
function rgb2hsv($rgb) {
$r = $rgb[‘red’] / 255; $g = $rgb[‘green’] / 255; $b = $rgb[‘blue’] / 255;
$max = max($r, $g, $b); $min = min($r, $g, $b);
$h = 0;
if ($max != $min) {
if ($max == $r) $h = 60 (($g – $b) / ($max – $min));
elseif ($max == $g) $h = 60 (($b – $r) / ($max – $min)) + 120;
else $h = 60 (($r – $g) / ($max – $min)) + 240;
}
return [‘h’ => ($h < 0 ? $h + 360 : $h), 's' => ($max == 0 ? 0 : ($max – $min) / $max), ‘v’ => $max];
}
?>
応用・注意点
・モノクロ画像の扱い: 彩度(S)が0のとき、色相(H)は計算上不安定になります。モノクロ画像を正しく扱うには、「彩度が一定以下の場合は色相の比較をスキップする」といった条件分岐をdistance関数に追加することをお勧めします。
・処理負荷: 16×16ピクセルへのリサイズは非常に高速ですが、精度が足りない場合は32×32にするなど調整してください。大量の画像を検索する場合は、あらかじめDBにHSV値を保存しておくことで、計算コストを大幅に削減できます。