前文
私が取り組んでいるプロジェクトの一環として、システム内の画像を検索するための便利な方法を提供しようとしています。現在、さまざまなタイプのユーザー追加メタデータ(タイトル、説明、キーワードなど)と、抽出したさまざまなメタデータ(EXIF、IPTC、XMPなど)による検索を提供しています。また、グーグルの画像検索で見られるような「カラー検索」を追加したいと思います。
プロジェクトはPHPを使用し、Imagemagick拡張機能を使用して画像をセグメント化および量子化し、画像から最も「重要な」色を抽出できます。私が確信している結果は完全にはわかりません。ここに着きましたが、それらはかなり正確で、何もないより確かに優れているようです。
問題
私が苦労しているのは、これらの重要な色を代表的な色のセットに変換することです。 Googleの画像検索 を見ると、12色のセットがあります。検出した色で画像にインデックスを付け、検索結果をそのようにファセットできるように、色の値を最も近い代表的な色に数学的に「丸め」たいと思います。
助言がありますか?
最初のステップは、比較する色を定義することです。
2番目のステップは、自分の色から前のステップで選択した色の1つまでの最小距離を見つけることです。その距離を測定できるようにするには、色をモデル化するためのユークリッド空間が必要です。
もちろん、簡単な選択はRGB空間です
そして2色間の距離C1(r1、g1、b1)およびC2(r2、g2、b2) だろう
sqrt((r1 --r2)2 +(g1 -g2)2 +(b1 -b2)2 )。
ただし、より高い精度が必要な場合は、HSLシリンダーの派生物であるHue-Chroma-Lightness双円錐空間を使用することをお勧めします。
RGB空間では、物事はR、G、Bのように単純で、それぞれが別々の軸上にあります。 HCLでは、各軸の座標を計算する必要があります。
まず、彩度(彩度とは少し異なります)を次のように計算します。
クロマ= max(赤、緑、青)-min(赤、緑、青)
次に、H、C、およびLの値を正規化して、Hが0から2(PIを掛けてラジアンを単位とする場合に円をカバーするため)、Cが0から1(三角円の半径)になるようにします。 Lは-1(黒)から1(白)になります。
次に、z = Lを変換せずに取得します。これは、画像から垂直軸に沿っていることが明らかだからです。
色の場合、彩度はz軸からの距離であり、色相は角度であることが簡単にわかります。だから私たちは得る
x = C * cos(H * PI) そして
y = C * sin(H * PI)
この時点で、x、y、zはすべて[-1、1]になり、2つの色の間の距離は、上記と同じ式を使用して、
sqrt((x1 - バツ2)2 +(y1 -y2)2 +(z1 -z2)2 )。
さらに精度を高め、人間の色覚に従って最も近い色を見つけるには、 CIE-L * ab モデリング空間を使用し、 これらのアルゴリズム のいずれかを使用して距離を計算します。原則は上記の2つのケースと同じですが、アルゴリズムのみがより複雑です。
更新(7年後)
最後に、xkcdはこの投稿で使用できるコミックを特集しました!
これは大まかなアイデアにすぎません。自分のニーズに合わせて微調整する必要があります。
基本的に、色はRGBとして記録されるため、16進文字列「#000000」から「#ffffff」、またはRGBセット「rgb(0,0,0)」から「rgb(255,255,255)」のいずれかとして記録されると思いました。 、およびこれらは交換可能/翻訳可能であり、これは単純な数学的丸めの問題です。
色の全範囲で、(16 * 16)*(16 * 16)*(16 * 16)= 256 * 256 * 256 = 16,777,216の可能な色があります。
色を最も近い1文字の16進値に丸めると、16 * 16 * 16 = 4,096の可能な色になります。まだ多すぎますが、近づいています。
色を1文字の値に丸めますが、それをさらに4(0,3,7、f)のいずれかに制限すると、4 * 4 * 4 = 32になります。私には十分に近い値です。
そこで、これを実現するために、非常に基本的なPHP関数を作成しました:
function coloround( $incolor ){
$inR = hexdec( $incolor{0}.$incolor{1} )+1;
$inG = hexdec( $incolor{2}.$incolor{3} )+1;
$inB = hexdec( $incolor{4}.$incolor{5} )+1;
# Round from 256 values to 16
$outR = round( $outR/16 );
$outG = round( $outG/16 );
$outB = round( $outB/16 );
# Round from 16 to 4
$outR = round( $outR/4 );
$outG = round( $outG/4 );
$outB = round( $outB/4 );
# Translate to Hex
$outR = dechex( max( 0 , $outR*4-1 ) );
$outG = dechex( max( 0 , $outG*4-1 ) );
$outB = dechex( max( 0 , $outB*4-1 ) );
# Output
echo sprintf( '<span style="background-color:#%s;padding:0 10px;"></span> > <span style="background-color:#%s;padding:0 10px;"></span>%s has been rounded to %s<br>' ,
$incolor , $outR.$outG.$outB ,
$incolor , $outR.$outG.$outB );
}
この関数は、16進文字列が渡されると、元の色のサンプルと省略された色のサンプルをエコーします。
Imagemagickが色を返す形式がわからないため、これは基本的な概念実証にすぎませんが、このロジックを利用して独自のロジックを作成できる場合があります。
次に、これらの32色から、類似した色をグループ化し(おそらく、そこには約8色の灰色があります)、残りに名前を付けて、ユーザーがそれらで検索できるようにします。
function getSimilarColors (color) {
var base_colors=["660000","990000","cc0000","cc3333","ea4c88","993399","663399","333399","0066cc","0099cc","66cccc","77cc33","669900","336600","666600","999900","cccc33","ffff00","ffcc33","ff9900","ff6600","cc6633","996633","663300","000000","999999","cccccc","ffffff"];
//Convert to RGB, then R, G, B
var color_rgb = hex2rgb(color);
var color_r = color_rgb.split(',')[0];
var color_g = color_rgb.split(',')[1];
var color_b = color_rgb.split(',')[2];
//Create an emtyp array for the difference betwwen the colors
var differenceArray=[];
//Function to find the smallest value in an array
Array.min = function( array ){
return Math.min.apply( Math, array );
};
//Convert the HEX color in the array to RGB colors, split them up to R-G-B, then find out the difference between the "color" and the colors in the array
$.each(base_colors, function(index, value) {
var base_color_rgb = hex2rgb(value);
var base_colors_r = base_color_rgb.split(',')[0];
var base_colors_g = base_color_rgb.split(',')[1];
var base_colors_b = base_color_rgb.split(',')[2];
//Add the difference to the differenceArray
differenceArray.Push(Math.sqrt((color_r-base_colors_r)*(color_r-base_colors_r)+(color_g-base_colors_g)*(color_g-base_colors_g)+(color_b-base_colors_b)*(color_b-base_colors_b)));
});
//Get the lowest number from the differenceArray
var lowest = Array.min(differenceArray);
//Get the index for that lowest number
var index = differenceArray.indexOf(lowest);
//Function to convert HEX to RGB
function hex2rgb( colour ) {
var r,g,b;
if ( colour.charAt(0) == '#' ) {
colour = colour.substr(1);
}
r = colour.charAt(0) + colour.charAt(1);
g = colour.charAt(2) + colour.charAt(3);
b = colour.charAt(4) + colour.charAt(5);
r = parseInt( r,16 );
g = parseInt( g,16 );
b = parseInt( b ,16);
return r+','+g+','+b;
}
//Return the HEX code
return base_colors[index];
}
探している色の数に応じて、有効桁数を減らすためにビット演算子(PHPのリファレンス ここ 、質問で言及したので)を使用してみませんか?シフトする前にRGB値を丸めて、精度を上げることもできます。