LestaD
@LestaD
Веб разработчик

Как определить цвет, близкий к цвету из существующего списка (php)?

У меня есть список разрешенных цветов. 68 цветов в #rgb
И есть также цвет пикселя в #rrggbb.
Необходимо определить самый близкий цвет из существующего списка.
  • Вопрос задан
  • 8522 просмотра
Пригласить эксперта
Ответы на вопрос 2
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
Переводим число из HEX.
// писал по памяти, не уверен что заведется
$rgb = array_map($component) {

    return base_convert($component, 16, 10);
}, array_chunk(ltrim($color, '#'), 2));

И теперь мы можем вычислить расстояние между цветами по формуле:
d=sqrt((r2-r1)^2+(g2-g1)^2+(b2-b1)^2)

По расстоянию находим наиболее близкий цвет.

Еще как вариант - перевести цвет в HSV и сравнивать по среднему значению компоненты HUE. Если у вас большая палитра, то будет дешевле в плане вычислений один раз перевести RGB в HSV и потом считать только среднее значение.

Итоговый вариант:
// преобразует HEX в RGB
function hexToRgb($hex) 
{
    return array_map($component) {

        return base_convert($component, 16, 10);
    }, array_chunk(ltrim($hex, '#'), 2));
}

// возвращает расстояние между двумя цветами
function getDistanceFromColor($a, $b) 
{
    list($r1, $g1, $b1) = $a;
    list($r2, $g2, $b2) = $b;
    
    return sqrt(pow($r2-$r1, 2)+pow($g2-$g1, 2)+pow($b2-$b1, 2));
}

// Возвращает наиболее подходящий цвет из палитры
function getClosestColor($color, array $pallet) {
    $distances = array_map(function ($colorFromPallet) use ($color) {
         return getDistanceFromColor($color, $colorFromPallet);
    }, $pallet);
    
    ksort($distances);
    $keys = array_keys($distances);
    
    return $pallet[$keys[0]];    
}

// пример:
$color = "#ff0044";
// для примера у нас есть палитра из HEX цветов
$pallet = ["#ff0000", "#ffffff"];
// сразу переводим в RGB всю палитру
$pallet = array_map(function ($color) {
    return hexToRgb($color);
}, $pallet);

// берем самый подходящий в палитре цвет
// он вернется в формате RGB, можете потом сконвертить в HEX
$closestColor = getClosestColor($color, $pallet);


для картинки можно сделать проще - пусть функция getClosestColor будет возвращать индекс цвета из палитры. Так проще организовать подсчет и мы все так же знаем на какой цвет заменять.
Ответ написан
iDevArtem
@iDevArtem
Исходя из ответа @Fesor и Wiki набросал некий код (не особо быстро работает, особенно для больших картинок, но имеет право на жизнь):

<?php

$result = array();
$colors = array('#ff0000', '#ffffaa', '#ff00ff', '#cde1b0', '#000000', '#ffffff', /* весь ваш список из 68 цветов */);
$sizeof = sizeof($colors);

$img = imagecreatefromjpeg("/img_path/img.jpg"); // если формат картинки jpg, jpeg
// $img = imagecreatefrompng("/img_path/img.png"); // если формат картинки png

$imgX = imagesx($img);
$imgY = imagesy($img); 

for ($x = 0; $x < $imgX; $x++) {
	for ($y = 0; $y < $imgY; $y++) { 
		$imgColor = imagecolorsforindex($img, imagecolorat($img, $x, $y));
		list($imgR, $imgG, $imgB) = array($imgColor['red'], $imgColor['green'], $imgColor['blue']);
		for ($i = 0; $i < $sizeof; $i++) {
			list($r, $g, $b) = array_map('hexdec', str_split(trim($colors[$i], '#'), 2));
			$delta = sqrt(pow($imgR-$r, 2)+pow($imgG-$g, 2)+pow($imgB-$b, 2));
			if ($delta >= 2 and $delta <= 2.5) { // 2.3 - примерно соответствует минимально различимому для человеческого глаза отличию между цветами (wiki)
				$result[$i] = $colors[$i];
			}
		}
	}
} 

print_r($result); // $result - список приближенно присутствующих цветов на картинке из списка $colors
?>
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы