2010-12-19 144 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