Подтвердить что ты не робот

"Округление" значений цвета до ближайшего из небольшого набора цветов

Преамбула

Как часть проекта, над которым я работаю, я пытаюсь обеспечить удобный способ поиска изображений в нашей системе. В настоящее время мы осуществляем поиск по различным типам добавленных пользователем метаданных (например, название, описание, ключевые слова) и различные метаданные, которые мы извлекаем (например, EXIF, IPTC, XMP и т.д.). Я также хотел бы добавить "цветной поиск", аналогичный тому, что вы можете увидеть в поиске изображений в Google.

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

Проблема

Бит, с которым я сталкиваюсь, превращает эти значительные цвета в набор репрезентативных цветов, например. когда вы смотрите поиск изображений Google, там есть набор из 12 цветов. Я хотел бы математически "округлить" значение цвета до ближайшего репрезентативного цвета, чтобы я мог индексировать изображение с помощью цветов, которые я обнаружил, а затем фасет мои результаты поиска таким образом.

Любые предложения?

4b9b3361

Ответ 1

Первым шагом будет определение цветов, с которыми вы хотите сравнить.

Второй шаг - найти наименьшее расстояние от вашего цвета до одного из цветов, которые вы выбрали на предыдущем шаге. Чтобы измерить это расстояние, вам понадобится евклидово пространство для моделирования цветов.

Конечно, простым выбором было бы пространство RGB

alt text

И расстояние между двумя цветами C 1 (r 1, g 1, b 1) и C 2 (r 2, g 2, b 2) будет

sqrt ((r 1 - r 2) 2 + (g 1 - g 2) 2 + (b 1 - b 2) 2).

Но если вам нужна более высокая точность, было бы лучше использовать пространство биона Хью-Хрома-Легкость, производное от цилиндра HSL.

alt text

В пространстве RGB вещи были прямолинейными, как R, G и B, где каждая на отдельной оси. В HCL нам нужно вычислить координаты на каждой оси.

Прежде всего, мы вычисляем цветность (которая немного отличается от насыщения) как:

Chroma = max (красный, зеленый, синий) - мин (красный, зеленый, синий)

Затем мы нормализуем наши значения H, C и L, так что H идет от 0 до 2 (чтобы покрыть круг, если мы умножим на PI и возьмем радианы в качестве единицы), C идет от 0 до 1 (радиус тригонометрический круг), а L - от -1 (черный) до 1 (белый).

Затем мы берем z = L без каких-либо преобразований, так как из изображения видно, что он идет по вертикальной оси.

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

x = C * cos (H * PI) и
y = C * sin (H * PI)

В этот момент x, y и z будут находиться в [-1, 1], а расстояние между двумя цветами будет, используя ту же формулу, что и выше,

sqrt ((x 1 - x 2) 2 + (y 1 - y 2) 2 + (z 1 - z 2) 2).


Чтобы получить еще большую точность и найти самый близкий цвет в соответствии с восприятием цвета человека, вы можете использовать CIE-L * ab пространство моделирования и вычислить расстояние с помощью одного из этих алгоритмов. Принципы те же, что и для двух рассмотренных выше случаев, только алгоритмы сложнее.


Обновление (через 7 лет)

Наконец, в xkcd появился комикс, который я могу использовать в этом сообщении!

введите описание изображения здесь

https://xkcd.com/1882/

Ответ 2

Это приблизительная идея - вам нужно будет настроить ее на собственные нужды.

В принципе, я думал, что, поскольку цвета записываются как RGB, либо в виде строки Hex "# 000000" на "#ffffff", либо в виде набора RGB "rgb (0,0,0)" до "rgb ( 255,255,255)", и они являются взаимозаменяемыми/переводными, это простая математическая проблема округления.

В полном диапазоне цветов будет (16 * 16) * (16 * 16) * (16 * 16) = 256 * 256 * 256 = 16 777 216 возможных цветов.

Округление цветов до их ближайшего единственного символа Значение Hex уменьшает это до 16 * 16 * 16 = 4 096 возможных цветов. Все еще слишком много, но все ближе.

Округление цветов до одного значения символа, но затем ограничение того, чтобы далее быть одним из 4 (0,3,7, f), уменьшает его до 4 * 4 * 4 = 32. Достаточно близко для меня.

Итак, я создал очень базовую функцию PHP, чтобы попытаться достичь этого:

function coloround( $incolor ){
  $inR = hexdec( $incolor{0}.$incolor{1} )+1;
  $inG = hexdec( $incolor{2}.$incolor{3} )+1;
  $inB = hexdec( $incolor{4}.$incolor{5} )+1;
 # Round from 256 values to 16
  $outR = round( $outR/16 );
  $outG = round( $outG/16 );
  $outB = round( $outB/16 );
 # Round from 16 to 4
  $outR = round( $outR/4 );
  $outG = round( $outG/4 );
  $outB = round( $outB/4 );
 # Translate to Hex
  $outR = dechex( max( 0 , $outR*4-1 ) );
  $outG = dechex( max( 0 , $outG*4-1 ) );
  $outB = dechex( max( 0 , $outB*4-1 ) );
 # Output
  echo sprintf( '<span style="background-color:#%s;padding:0 10px;"></span> &gt; <span style="background-color:#%s;padding:0 10px;"></span>%s has been rounded to %s<br>' ,
         $incolor , $outR.$outG.$outB ,
         $incolor , $outR.$outG.$outB );
}

Эта функция, когда передана шестнадцатеричная строка, отображает образец исходного цвета и образец сокращенного цвета.

Это просто базовое доказательство концепции, поскольку я не знаю формат Imagemagick, который возвращает цвета, но вы можете использовать эту логику для создания своего собственного.

Из этих 32 цветов вы можете сгруппировать похожие (там, вероятно, будет около 8 оттенков серого) и назовите оставшуюся часть, чтобы пользователи могли их искать.

Ответ 3

function getSimilarColors (color) {
    var base_colors=["660000","990000","cc0000","cc3333","ea4c88","993399","663399","333399","0066cc","0099cc","66cccc","77cc33","669900","336600","666600","999900","cccc33","ffff00","ffcc33","ff9900","ff6600","cc6633","996633","663300","000000","999999","cccccc","ffffff"];

    //Convert to RGB, then R, G, B
    var color_rgb = hex2rgb(color);
    var color_r = color_rgb.split(',')[0];
    var color_g = color_rgb.split(',')[1];
    var color_b = color_rgb.split(',')[2];

    //Create an emtyp array for the difference betwwen the colors
    var differenceArray=[];

    //Function to find the smallest value in an array
    Array.min = function( array ){
           return Math.min.apply( Math, array );
    };


    //Convert the HEX color in the array to RGB colors, split them up to R-G-B, then find out the difference between the "color" and the colors in the array
    $.each(base_colors, function(index, value) { 
        var base_color_rgb = hex2rgb(value);
        var base_colors_r = base_color_rgb.split(',')[0];
        var base_colors_g = base_color_rgb.split(',')[1];
        var base_colors_b = base_color_rgb.split(',')[2];

        //Add the difference to the differenceArray
        differenceArray.push(Math.sqrt((color_r-base_colors_r)*(color_r-base_colors_r)+(color_g-base_colors_g)*(color_g-base_colors_g)+(color_b-base_colors_b)*(color_b-base_colors_b)));
    });

    //Get the lowest number from the differenceArray
    var lowest = Array.min(differenceArray);

    //Get the index for that lowest number
    var index = differenceArray.indexOf(lowest);

    //Function to convert HEX to RGB
    function hex2rgb( colour ) {
        var r,g,b;
        if ( colour.charAt(0) == '#' ) {
            colour = colour.substr(1);
        }

        r = colour.charAt(0) + colour.charAt(1);
        g = colour.charAt(2) + colour.charAt(3);
        b = colour.charAt(4) + colour.charAt(5);

        r = parseInt( r,16 );
        g = parseInt( g,16 );
        b = parseInt( b ,16);
        return r+','+g+','+b;
    }

    //Return the HEX code
    return base_colors[index];
}

Ответ 4

В зависимости от количества цветов, которые вы ищете, почему бы не попробовать использовать побитовые операторы (ссылка PHP здесь, так как вы упомянули об этом в вопрос) уменьшить количество значимых цифр? Вы можете объединить значения RGB перед переключением, чтобы повысить точность.