2010-12-19 227 views
11

编辑:RGB最接近预定义的颜色

随着给出的答案我做了这个功能

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是一个用户定义函数,但我想做到的是改变这种功能与功能抢最接近的颜色。 $ colorofimage包含每个像素的数组信息与十六进制:x:y 我想要的是rgb2hex(NEWFUNCTION($ r,$ g,$ b)); 因此,新的十六进制是预定义数组中的1。

我希望你能理解,因为我不知道如何去做,因为我不知道颜色的算法。

+2

取决于如何努力了,你想去的地方,在我的(类似的)问题的答案可能是有用的 - http://stackoverflow.com/questions/4057475/rounding-colour-values-to-the-最近的一组颜色 – 2010-12-19 22:47:09

+0

同样,我不太了解PHP,但是作为解决方案给出的函数看起来效率远低于我之前建议的那种:http:// stackoverflow .com/questions/4485229/rgb-to-closest-predefined-color/4485327#4485327 – beldaz 2010-12-20 03:45:45

+1

为什么在foreach循环中有返回? – Joeri 2015-09-20 13:47:17

回答

15

你必须计算每种颜色的距离,并选择最小的。

有几种方法可以做到这一点。一种简单的方法是计算的距离将是:

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 
+0

感谢你们,我即将编辑OP,并把我所做的功能。 – Ugleh 2010-12-19 22:39:11

+3

随机想法:严格地说,如果性能是一个问题,您不需要取平方根,只需找到最小的方形。 – 2010-12-19 22:40:48

+0

@mootinator为什么我们需要对所有的值进行平方?为什么不只是最短的距离? – 2013-06-23 19:53:59

1

计算从输入颜色到调色板的所有可能候选者的距离,然后选择距离最近的那个距离替换它。

距离可以用任何你喜欢的方式定义;欧几里得距离似乎适用于RGB立方体,圆柱体或HSL/HSV锥体。

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)的心理视觉演绎的更好的帐户,但我怀疑这是更比你所需要的。

+0

即使通常这样做,它也不一定是立方体。它主要是一个向量空间,也可能是一个平行线。 – user502515 2010-12-20 02:12:20

+0

@ user502515:它可以,但不太可能。我遇到的所有RGB色彩空间(最明显的是sRGB)倾向于在0到1或0到255的范围内。其他色彩空间(例如CIE Lab)确实有不同的边界,但它们不是RGB。 – beldaz 2010-12-20 03:40:44

+0

@beldaz我们为什么要平均距离呢?为什么不把每种颜色的距离总和? – 2013-06-23 19:40:00

0

没有采取平方根。寻找最短的距离没有一点是一样的发现了最短的平方距离。sqrt是一个昂贵的操作,所以就跳过它。

无论是真的当然,事情取决于你的程序多久一次我做这个计算,但它仍然没有意义。

5

由于这个问题显示在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; 
} 
+0

对我来说这是唯一正确的答案! – 2016-06-22 12:02:33

+0

请注意,在上面的代码中使用了[用于Delta E算法的替换](https://en.wikipedia.org/wiki/Color_difference)。尽管如此,如果我今天要实现一个新的色距函数,我会使用DIN99c或DIN99d色彩空间,因为它们比CIE94或CIEDE2000更容易且更快速地计算,同时提供相似的质量。 – xong 2017-03-12 10:14:52

+0

一些解释是为了。这是做什么的?为什么它更好? – Raphael 2017-04-01 07:26:11