Следовать за:Нахождение точного «расстояния» между цветами

StackOverflow https://stackoverflow.com/questions/1313

  •  08-06-2019
  •  | 
  •  

Вопрос

Оригинальный вопрос

Я ищу функцию, которая пытается количественно определить, насколько «удалены» (или различны) два цвета.Этот вопрос на самом деле состоит из двух частей:

  1. Какое цветовое пространство лучше всего отражает человеческое зрение?
  2. Какой показатель расстояния в этом пространстве лучше всего отражает человеческое зрение (евклидово?)
Это было полезно?

Решение

Преобразуйте в La*b* (он же просто «Lab», и вы также увидите ссылку на «CIELAB»).Хорошим быстрым способом измерения разницы в цвете является

(L1-L2)^2 + (a1-a2)^2 + (b1-b2)^2

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

А a и b значения представляют противоположные цвета аналогично тому, как работают колбочки, и могут быть отрицательными или положительными.Нейтральные цвета – белый, серый. a=0,b=0L яркость определяется определенным образом, от нуля (чистая темнота) до чего угодно.

Грубое объяснение:>> Учитывая цвет, наши глаза различают два широких диапазона длин волн: синий и более длинные волны.а затем, благодаря более поздней генетической мутации, более длинноволновые колбочки разделились на две части, различая для нас красный и красный.зеленый.

Кстати, для вашей карьеры будет здорово подняться над коллегами-пещерными людьми, которые знают только «RGB» или «CMYK», которые отлично подходят для устройств, но не подходят для серьезной работы по восприятию.Я работал на учёных, занимающихся визуализацией, которые ничего об этом не знали!

Чтобы получить больше удовольствия от чтения теории цветовых различий, попробуйте:

Более подробная информация о лаборатории на сайте http://en.kioskea.net/video/cie-lab.php3 В настоящее время я не могу найти нормальную страницу, на которой действительно были бы формулы преобразования, но я уверен, что кто-то отредактирует этот ответ, включив в него одну.

Другие советы

поскольку ссылка на cmetric.htm выше для меня не удалась, как и многие другие реализации цветового расстояния, которые я нашел (после очень долгого путешествия...), как рассчитать лучшее цветовое расстояние, и...наиболее научно точный: дельтаЕ и из 2 значений RGB (!) с использованием OpenCV:

Для этого потребовалось 3 преобразования цветового пространства + некоторое преобразование кода из javascript (http://svn.int64.org/viewvc/int64/colors/colors.js) на С++

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

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/photo/photo.hpp>
#include <math.h>

using namespace cv;
using namespace std;

#define REF_X 95.047; // Observer= 2°, Illuminant= D65
#define REF_Y 100.000;
#define REF_Z 108.883;

void bgr2xyz( const Vec3b& BGR, Vec3d& XYZ );
void xyz2lab( const Vec3d& XYZ, Vec3d& Lab );
void lab2lch( const Vec3d& Lab, Vec3d& LCH );
double deltaE2000( const Vec3b& bgr1, const Vec3b& bgr2 );
double deltaE2000( const Vec3d& lch1, const Vec3d& lch2 );


void bgr2xyz( const Vec3b& BGR, Vec3d& XYZ )
{
    double r = (double)BGR[2] / 255.0;
    double g = (double)BGR[1] / 255.0;
    double b = (double)BGR[0] / 255.0;
    if( r > 0.04045 )
        r = pow( ( r + 0.055 ) / 1.055, 2.4 );
    else
        r = r / 12.92;
    if( g > 0.04045 )
        g = pow( ( g + 0.055 ) / 1.055, 2.4 );
    else
        g = g / 12.92;
    if( b > 0.04045 )
        b = pow( ( b + 0.055 ) / 1.055, 2.4 );
    else
        b = b / 12.92;
    r *= 100.0;
    g *= 100.0;
    b *= 100.0;
    XYZ[0] = r * 0.4124 + g * 0.3576 + b * 0.1805;
    XYZ[1] = r * 0.2126 + g * 0.7152 + b * 0.0722;
    XYZ[2] = r * 0.0193 + g * 0.1192 + b * 0.9505;
}

void xyz2lab( const Vec3d& XYZ, Vec3d& Lab )
{
    double x = XYZ[0] / REF_X;
    double y = XYZ[1] / REF_X;
    double z = XYZ[2] / REF_X;
    if( x > 0.008856 )
        x = pow( x , .3333333333 );
    else
        x = ( 7.787 * x ) + ( 16.0 / 116.0 );
    if( y > 0.008856 )
        y = pow( y , .3333333333 );
    else
        y = ( 7.787 * y ) + ( 16.0 / 116.0 );
    if( z > 0.008856 )
        z = pow( z , .3333333333 );
    else
        z = ( 7.787 * z ) + ( 16.0 / 116.0 );
    Lab[0] = ( 116.0 * y ) - 16.0;
    Lab[1] = 500.0 * ( x - y );
    Lab[2] = 200.0 * ( y - z );
}

void lab2lch( const Vec3d& Lab, Vec3d& LCH )
{
    LCH[0] = Lab[0];
    LCH[1] = sqrt( ( Lab[1] * Lab[1] ) + ( Lab[2] * Lab[2] ) );
    LCH[2] = atan2( Lab[2], Lab[1] );
}

double deltaE2000( const Vec3b& bgr1, const Vec3b& bgr2 )
{
    Vec3d xyz1, xyz2, lab1, lab2, lch1, lch2;
    bgr2xyz( bgr1, xyz1 );
    bgr2xyz( bgr2, xyz2 );
    xyz2lab( xyz1, lab1 );
    xyz2lab( xyz2, lab2 );
    lab2lch( lab1, lch1 );
    lab2lch( lab2, lch2 );
    return deltaE2000( lch1, lch2 );
}

double deltaE2000( const Vec3d& lch1, const Vec3d& lch2 )
{
    double avg_L = ( lch1[0] + lch2[0] ) * 0.5;
    double delta_L = lch2[0] - lch1[0];
    double avg_C = ( lch1[1] + lch2[1] ) * 0.5;
    double delta_C = lch1[1] - lch2[1];
    double avg_H = ( lch1[2] + lch2[2] ) * 0.5;
    if( fabs( lch1[2] - lch2[2] ) > CV_PI )
        avg_H += CV_PI;
    double delta_H = lch2[2] - lch1[2];
    if( fabs( delta_H ) > CV_PI )
    {
        if( lch2[2] <= lch1[2] )
            delta_H += CV_PI * 2.0;
        else
            delta_H -= CV_PI * 2.0;
    }

    delta_H = sqrt( lch1[1] * lch2[1] ) * sin( delta_H ) * 2.0;
    double T = 1.0 -
            0.17 * cos( avg_H - CV_PI / 6.0 ) +
            0.24 * cos( avg_H * 2.0 ) +
            0.32 * cos( avg_H * 3.0 + CV_PI / 30.0 ) -
            0.20 * cos( avg_H * 4.0 - CV_PI * 7.0 / 20.0 );
    double SL = avg_L - 50.0;
    SL *= SL;
    SL = SL * 0.015 / sqrt( SL + 20.0 ) + 1.0;
    double SC = avg_C * 0.045 + 1.0;
    double SH = avg_C * T * 0.015 + 1.0;
    double delta_Theta = avg_H / 25.0 - CV_PI * 11.0 / 180.0;
    delta_Theta = exp( delta_Theta * -delta_Theta ) * ( CV_PI / 6.0 );
    double RT = pow( avg_C, 7.0 );
    RT = sqrt( RT / ( RT + 6103515625.0 ) ) * sin( delta_Theta ) * -2.0; // 6103515625 = 25^7
    delta_L /= SL;
    delta_C /= SC;
    delta_H /= SH;
    return sqrt( delta_L * delta_L + delta_C * delta_C + delta_H * delta_H + RT * delta_C * delta_H );
}

Надеюсь, это кому-то поможет :)

HSL и HSV лучше воспринимают цвета человека.В соответствии с Википедия:

Иногда при работе с художественными материалами, оцифрованными изображениями или другими носителями предпочтительнее использовать цветовую модель HSV или HSL вместо альтернативных моделей, таких как RGB или CMYK, из-за различий в том, как модели имитируют восприятие цвета людьми.RGB и CMYK — это аддитивные и субтрактивные модели соответственно, моделирующие способ объединения основных цветных источников света или пигментов (соответственно) с образованием новых цветов при смешивании.

Graphical depiction of HSV

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

Может показаться спамом, но нет, эта ссылка действительно интересна для цветовых пространств :)

http://www.compuphase.com/cmetric.htm

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

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

ИзображениеМагия предоставляет следующие шкалы:

  • красный:0,3
  • зеленый:0,6
  • синий:0,1

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

Что ж, в первую очередь я бы сказал, что общие показатели HSV (оттенок, насыщенность и значение) или HSL лучше отражают то, как люди воспринимают цвет, чем, скажем, RGB или CYMK.Видеть HSL, HSV в Википедии.

Я предполагаю, что наивно я бы нанес точки в пространстве HSL для двух цветов и вычислил величину вектора разницы.Однако это означало бы, что ярко-желтый и ярко-зеленый будут считаться такими же разными, как зеленый и темно-зеленый.Но многие считают красный и розовый двумя разными цветами.

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

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

Еще лучше, вы могли бы посмотреть, делал ли кто-нибудь подобное в Интернете...

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top