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 – это функция, определяемая пользователем, но я хочу, чтобы она изменила эту функцию с помощью функции, чтобы захватить ближайший цвет.

$ colorsofimage содержит массив каждой информации о пикселях с шестнадцатеричным: x: y, которым я хочу, это rgb2hex (NEWFUNCTION ($ r, $ g, $ b)); Так что новый hex является 1 из предопределенного массива.

Надеюсь, вы поняли, потому что я понятия не имею, как это сделать, потому что я не знаю алгоритм цвета.

Вы должны рассчитать расстояние до каждого цвета и выбрать самый маленький.

Есть несколько способов сделать это. Простым методом было бы рассчитать расстояние:

 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 

Цветовое пространство RGB – это просто куб. В 24-битном цвете каждая сторона имеет длину 256, позволяя значения от 0 до 255. Чтобы найти ближайший цвет внутри этого куба, вам нужна функция расстояния. Простейшим и интуитивным является евклидово расстояние : если у вас есть цвет (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 ), но я подозреваю, что это больше, чем вам нужно.

Поскольку этот вопрос отображается в первой десятке результатов поиска 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 * (eg "ff0000", "4da4f3" or "#b5d7f3") * or arrays with values for red, green and blue * (eg $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; } 

Вычислите расстояние от входного цвета до всех возможных кандидатов вашей палитры, а затем выберите тот, который находится на самом маленьком расстоянии, как тот, который заменит его.

Расстояние можно определить любым способом; Евклидовое расстояние кажется пригодным для кубиков RGB, цилиндров или конусов HSL / HSV.

Нет смысла брать квадратный корень. Найти кратчайшее расстояние – это то же самое, что найти кратчайшее квадратное расстояние. sqrt – дорогостоящая операция, поэтому просто пропустите ее.

Неважно, действительно ли это имеет значение, зависит от того, как часто ваша программа будет делать этот расчет, но все равно бессмысленно это иметь.