修改
给出答案我做了这个功能
function grabclosestcolor($r, $g, $b){
$colors = array(array(124,12,12),array(7,7,11),array(110,224,219),array(123,123,123),array(124,177,74),array(130,86,53),array(77,77,77),array(164,124,68),array(204,196,132),array(164,148,147),array(163,123,67),array(26,122,26), array(195,195,50),array(193,193,193),array(255,248,73),array(243,243,243));
$differencearray = array();
foreach ($colors as $value) {
$difference = sqrt(pow($r-$value[0],2)+pow($g-$value[1],2)+pow($b-$value[2],2));
array_push($differencearray, $difference);
$smallest = min($differencearray);
$key = array_search($smallest, $differencearray);
return $colors[$key];
}
}
<小时/> 我的目标是这个。我抓住一张图片并遍历每个像素并抓住它的x,y和rgb。
我没有抓住rgb,而是有一个预定义的数组,我正在寻找最接近的匹配,从我抓到的预定义数组。 这里的目标是仅使用预定义数组中的颜色。 这是我的颜色阵列。
$colors = array(array(124,12,12),array(7,7,11),array(110,224,219),array(123,123,123),array(124,177,74),array(130,86,53),array(77,77,77),array(164,124,68),array(204,196,132),array(164,148,147),array(163,123,67),array(26,122,26), array(195,195,50),array(193,193,193),array(255,248,73),array(243,243,243));
这是我现有的代码循环遍历所有。
$int = imagesx($im) - 1;
$int2 = imagesy($im) - 1;
$start2 = 0;
do{
$start = 0;
do{
$rgb = imagecolorat($im, $start, $start2);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$value = rgb2hex($r,$g,$b).":$start:$start2";
array_push($colorsofimage, $value);
} while($int > $start++);
} while($int2 > $start2++);
rgb2hex是一个用户自定义函数,但我想要完成的是用函数更改该函数以获取最接近的颜色。
我希望你理解,因为我不知道怎么做,因为我不知道颜色的算法。
答案 0 :(得分:16)
您必须计算每种颜色的距离,并选择最小的颜色。
有几种方法可以做到这一点。一种简单的方法是计算距离:
sqrt((r-r1)^2+(g-g1)^2+(b-b1)^2)
更好的方法可能是合并加权值来计算距离,例如转换RGB时使用的值 - > YUV:
Y = 0.299 * R + 0.587 * G + 0.114 * B
在这种情况下你会使用
sqrt(((r - r1) * .299)^2 + ((g - g1) * .587)^2 + ((b - b1) * .114)^2)
当然,既然你不需要确切的距离,只需要进行比较,你可以而且可能应该跳过平方根,进行最后的计算:
((r - r1) * .299)^2 + ((g - g1) * .587)^2 + ((b - b1) * .114)^2
答案 1 :(得分:11)
RGB colour-space只是一个立方体。在24位颜色中,每一边的长度为256,允许从0到255的值。要在此立方体中找到最接近的颜色,您需要一个距离函数。最简单和最直观的是Euclidean distance:如果你有颜色(r1,g1,b1)和另一种颜色(r2,g2,b2),距离将是sqrt((r2-r1)^2 + (g2-g1)^2 + (b2-b1)^2)
。
然后,您面临的挑战是找到预定义数组中所有值的最佳匹配。我建议您只需迭代所有值并依次检查每个值的距离即可。请注意,为此目的,您不需要执行sqrt
,只需比较平方和就足够了,并且可以全部基于整数数学。我的PHP不是很好,但大致你会这样做:
function dist($col1,$col2) {
$delta_r = $col1[0] - $col2[0];
$delta_g = $col1[1] - $col2[1];
$delta_b = $col1[2] - $col2[2];
return $delta_r * $delta_r + $delta_g * $delta_g + $delta_b * $delta_b;
}
$closest=$colors[0];
$mindist=dist($rgb,$colors[0]);
$ncolors=sizeof($colors);
for($i = 1; $i < $ncolors; ++$i)
{
$currdist = dist($rgb,$colors[$i]);
if($currdist<$mindist) {
$mindist=$currdist;
$closest=$colors[$i];
}
}
有更复杂的距离函数(例如,更好地考虑色差的心理视觉解释(查看Delta E)但我怀疑这比您需要的更多。
答案 2 :(得分:6)
由于这个问题显示在goolge搜索结果的前十位,这是我几年前写的一个更复杂的函数,它比现有的PHP函数产生了更好的结果。
/*
* Die Funktion gibt den Array-Schlüssel der Farbe ($palette),
* die am ehesten der Farbe $givenColor entspricht.
*
* Returns the index of the palette-color which is most similar
* to $givenColor.
*
* $givenColor und die Einträge in $palette können entweder
* Strings im Format (#)rrggbb
* (z. B. "ff0000", "4da4f3" oder auch "#b5d7f3")
* oder Arrays mit je einem Wert für Rot, Grün und Blau
* (z. B. $givenColor = array( 0xff, 0x00, 0x00 ) )
* sein.
*
* $givenColor and the colors in $palette should be either
* formatted as (#)rrggbb
* (e. g. "ff0000", "4da4f3" or "#b5d7f3")
* or arrays with values for red, green and blue
* (e. g. $givenColor = array( 0xff, 0x00, 0x00 ) )
*
* Referenzen/References:
* function rgb2lab
* - http://www.f4.fhtw-berlin.de/~barthel/ImageJ/ColorInspector//HTMLHilfe/farbraumJava.htm
* - http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
* - http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
*
* function deltaE
* - http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CMC.html
*/
function getNearestColor( $givenColor,
$palette = array('blue' => '0000ff','red' => 'ff0000','green' => '00ff00','yellow' => 'ffff00','black' => '000000','white' => 'ffffff','orange' => 'ff8800','purple' => 'ff00ff', 'teal' => '00ffff')
)
{
if(!function_exists('rgb2lab'))
{
function rgb2lab($rgb) {
$eps = 216/24389; $k = 24389/27;
// reference white D50
$xr = 0.964221; $yr = 1.0; $zr = 0.825211;
// reference white D65
#$xr = 0.95047; $yr = 1.0; $zr = 1.08883;
// RGB to XYZ
$rgb[0] = $rgb[0]/255; //R 0..1
$rgb[1] = $rgb[1]/255; //G 0..1
$rgb[2] = $rgb[2]/255; //B 0..1
// assuming sRGB (D65)
$rgb[0] = ($rgb[0] <= 0.04045)?($rgb[0]/12.92):pow(($rgb[0]+0.055)/1.055,2.4);
$rgb[1] = ($rgb[1] <= 0.04045)?($rgb[1]/12.92):pow(($rgb[1]+0.055)/1.055,2.4);
$rgb[2] = ($rgb[2] <= 0.04045)?($rgb[2]/12.92):pow(($rgb[2]+0.055)/1.055,2.4);
// sRGB D50
$x = 0.4360747*$rgb[0] + 0.3850649*$rgb[1] + 0.1430804*$rgb[2];
$y = 0.2225045*$rgb[0] + 0.7168786*$rgb[1] + 0.0606169*$rgb[2];
$z = 0.0139322*$rgb[0] + 0.0971045*$rgb[1] + 0.7141733*$rgb[2];
// sRGB D65
/*$x = 0.412453*$rgb[0] + 0.357580*$rgb[1] + 0.180423*$rgb[2];
$y = 0.212671*$rgb[0] + 0.715160*$rgb[1] + 0.072169*$rgb[2];
$z = 0.019334*$rgb[0] + 0.119193*$rgb[1] + 0.950227*$rgb[2];*/
// XYZ to Lab
$xr = $x/$xr; $yr = $y/$yr; $zr = $z/$zr;
$fx = ($xr > $eps)?pow($xr, 1/3):($fx = ($k * $xr + 16) / 116); $fy = ($yr > $eps)?pow($yr, 1/3):($fy = ($k * $yr + 16) / 116); $fz = ($zr > $eps)?pow($zr, 1/3):($fz = ($k * $zr + 16) / 116);
$lab = array();
$lab[] = round(( 116 * $fy ) - 16); $lab[] = round(500*($fx-$fy)); $lab[] = round(200*($fy-$fz));
return $lab;
} // function rgb2lab
}
if(!function_exists('deltaE'))
{
function deltaE($lab1, $lab2)
{
// CMC 1:1
$l = 1; $c = 1;
$c1 = sqrt($lab1[1]*$lab1[1]+$lab1[2]*$lab1[2]); $c2 = sqrt($lab2[1]*$lab2[1]+$lab2[2]*$lab2[2]);
$h1 = (((180000000/M_PI) * atan2($lab1[1],$lab1[2]) + 360000000) % 360000000)/1000000;
$t = (164 <= $h1 AND $h1 <= 345)?(0.56 + abs(0.2 * cos($h1+168))):(0.36 + abs(0.4 * cos($h1+35)));
$f = sqrt(pow($c1,4)/(pow($c1,4) + 1900));
$sl = ($lab1[0] < 16)?(0.511):((0.040975*$lab1[0])/(1 + 0.01765*$lab1[0]));
$sc = (0.0638 * $c1)/(1 + 0.0131 * $c1) + 0.638;
$sh = $sc * ($f * $t + 1 -$f);
return sqrt( pow(($lab1[0]-$lab2[0])/($l * $sl),2) + pow(($c1-$c2)/($c * $sc),2) + pow(sqrt(($lab1[1]-$lab2[1])*($lab1[1]-$lab2[1]) + ($lab1[2]-$lab2[2])*($lab1[2]-$lab2[2]) + ($c1-$c2)*($c1-$c2))/$sh,2) );
} // function deltaE
}
if(!function_exists('colorDistance'))
{
function colorDistance($lab1,$lab2)
{
return sqrt(($lab1[0]-$lab2[0])*($lab1[0]-$lab2[0])+($lab1[1]-$lab2[1])*($lab1[1]-$lab2[1])+($lab1[2]-$lab2[2])*($lab1[2]-$lab2[2]));
}
}
if(!function_exists('str2rgb'))
{
function str2rgb($str)
{
$str = preg_replace('~[^0-9a-f]~','',$str);
$rgb = str_split($str,2);
for($i=0;$i<3;$i++)
$rgb[$i] = intval($rgb[$i],16);
return $rgb;
} // function str2rgb
}
// split into RGB, if not already done
$givenColorRGB = is_array($givenColor)?$givenColor:str2rgb($givenColor);
$min = 0xffff;
$return = NULL;
foreach($palette as $key => $color)
{
// split into RGB
$color = is_array($color)?$color:str2rgb($color);
// deltaE
#if($min >= ($deltaE = deltaE(rgb2lab($color),rgb2lab($givenColorRGB))))
// euclidean distance
if($min >= ($deltaE = colorDistance(rgb2lab($color),rgb2lab($givenColorRGB))))
{
$min = $deltaE;
$return = $key;
}
}
return $return;
}
答案 3 :(得分:1)
计算从输入颜色到调色板的所有可能候选者的距离,然后选择距离最小的那个作为替换它的那个。
距离可以任何你喜欢的方式定义;欧几里德距离似乎适用于RGB立方体,圆柱体或HSL / HSV锥体。
答案 4 :(得分:0)
取平方根没有意义。找到最短距离与找到最短的平方距离相同。 sqrt
是一项昂贵的操作,所以请跳过它。
当然,它是否真的重要,取决于你的程序进行计算的频率,但它仍然没有意义。