Не идеальная реализация (это будет медленно, особенно на больших изображениях), но мы могли бы сделать что-то вроде:
$resource = imagecreatefrompng("test.png"); // Load our image (use imagecreatefromjpeg if using jpeg)
$width = imagesx($resource); // Get image width
$height = imagesy($resource); // Get image height
$results = []; // Create empty array to hold our "unique" color results
$tolerance = 5; // The amount of variation allowed to consider a color the same
// Loop each pixel in the image
for($x = 0; $x < $width; $x++) {
for($y = 0; $y < $height; $y++) {
$add = true;
$color_index = imagecolorat($resource, $x, $y);
$color = imagecolorsforindex($resource, $color_index); // Get the color as an array
// Push the first pixel into our results so we have something to start the comparison against
if (count($results) == 0){
$results[] = $color;
} else {
// Compare the current colour to our results using the tolerance
foreach ($results as $i => &$result):
$near_red = ($color['red'] > ($result['red'] - $tolerance)) && ($color['red'] < ($result['red'] + $tolerance));
$near_green = ($color['green'] > ($result['green'] - $tolerance)) && ($color['green'] < ($result['green'] + $tolerance));
$near_blue = ($color['blue'] > ($result['blue'] - $tolerance)) && ($color['blue'] < ($result['blue'] + $tolerance));
if ($near_red && $near_green && $near_blue){
// This colour is similar to another result
$add = false;
}
if (!$add){
break;
}
endforeach;
if ($add){
$results[] = $color;
}
}
}
}
// Output the unique colors visually. You would probably just count($results) here
foreach($results as $item):
$color = $item['red'] . "," . $item['green'] . "," . $item['blue'] . "," . "1";
echo "<div style='width: 10px; height: 10px; background-color: rgba($color); display: inline-block;'></div>";
endforeach;
Используя это изображение (которое содержит 8 уникальных цветов):
и допуск 75
Мы получаем выходные данные следующих 4 цветов:
С допуском 10
мы получаем следующие 8 цветов:
Поскольку мы l oop каждый В пикселе на изображении мы получаем значения цвета в формате RGBA, например:
[
"red" => 255,
"green" => 255,
"blue" => 255,
"alpha" => 255,
]
Затем мы можем сравнивать текущий пиксель с каждым элементом в нашем массиве $results
, добавляя и вычитая допуск и проверяя, является ли текущий Канал (красный, зеленый и синий) попадает в диапазон:
$near_red = ($color['red'] > ($result['red'] - $tolerance)) && ($color['red'] < ($result['red'] + $tolerance));
Например, если текущее значение пикселов красного цвета равно 50, а наш допуск равен 5, мы будем проверять, есть ли элемент в нашем $results
с красным значением от 45 до 55. Мы делаем то же самое для зеленого и синего. Если все 3 канала находятся в пределах диапазона, это означает, что у нас уже есть аналогичный цвет в нашем $results
, поэтому мы не добавляем текущий цвет пикселя в массив $results
.
Больше $tolerance
значение будет совпадать с менее похожими цветами. Значение $tolerance
, равное 0
, проверит точное соответствие и даст в нашем $results
гораздо больше уникальных цветов
Если аналогичного значения не существует, мы помещаем sh текущее значение RGBA пикселя в наш массив $results
.
После завершения выполнения кода массив $results
будет содержать «уникальные» цвета. Мы можем посчитать цвета следующим образом:
echo count($results);
Это решение может использовать большую оптимизацию (оно будет очень медленным на больших изображениях с большим количеством уникальных цветов), и я проигнорировал альфа-канал в этой реализации (достаточно легко добавить в случае необходимости), но, надеюсь, это полезно для вас.
Наихудшая сложность этой реализации -
O(n<sup>2</sup>)
, которая произойдет, если каждый цвет пикселя в изображении будет уникальным, что приведет к тому, что наш массив
$results
будет содержать каждое предыдущее значение, поэтому каждая итерация будет содержать вложенное l oop числа пикселей в изображении (более или менее), что для большого изображения будет невероятно медленным.