language agnostic - Seguimiento: Encontrar una "distancia" precisa entre los colores
language-agnostic colors (8)
Estoy buscando una función que intente cuantificar cuán "distantes" (o distintos) dos colores son. Esta pregunta es realmente en dos partes:
- ¿Qué espacio de color representa mejor la visión humana?
- ¿Qué distancia métrica en ese espacio representa mejor la visión humana (euclidiana?)
Como alguien que es daltónico, creo que es bueno tratar de agregar más separación que la visión normal. La forma más común de daltonismo es la deficiencia de rojo / verde. No significa que no se puede ver rojo o verde, significa que es más difícil de ver y más difícil de ver las diferencias. Por lo tanto, se necesita una separación mayor antes de que una persona daltónica sepa la diferencia.
Bueno, como primer punto de referencia, diría que las métricas comunes HSV (Hue, Saturation y Value) o HSL son mejor representativas de cómo los humanos perciben el color que RGB o CYMK. Ver HSL, HSV en Wikipedia .
Supongo que ingenuamente trazaré los puntos en el espacio HSL para los dos colores y calcularé la magnitud del vector de diferencia. Sin embargo, esto significaría que el amarillo brillante y el verde brillante se considerarían tan diferentes como el verde o el verde oscuro. Pero luego muchos consideran rojo y rosa dos colores diferentes.
Además, los vectores de diferencia en la misma dirección en este espacio de parámetros no son iguales. Por ejemplo, el ojo humano toma el verde mucho mejor que otros colores. Un cambio en matiz de verde en la misma cantidad que un cambio de rojo puede parecer mayor. También un cambio en la saturación de una pequeña cantidad a cero es la diferencia entre el gris y el rosa, en otros lugares el cambio sería la diferencia entre dos tonos de rojo.
Desde el punto de vista de los programadores, necesitarías trazar los vectores de diferencia, pero modificados por una matriz de proporcionalidad que ajustaría las longitudes según corresponda en varias regiones del espacio de HSL; esto sería bastante arbitrario y estaría basado en varias ideas de teoría de color, pero debe ajustarse de forma bastante arbitraria según lo que desee aplicar a esto.
Aún mejor, podrías ver si alguien ya hizo algo así en línea ...
La distancia más fácil sería, por supuesto, considerar los colores como vectores en 3D que se originan en el mismo origen y tomar la distancia entre sus puntos finales.
Si necesita considerar factores tales que el verde es más prominente al juzgar la intensidad, puede ponderar los valores.
ImageMagic proporciona las siguientes escalas:
- rojo: 0.3
- verde: 0.6
- azul: 0.1
Por supuesto, valores como este solo serían significativos en relación con otros valores para otros colores, no como algo que sería significativo para los humanos, así que todo lo que podrías usar para los valores sería el orden de similiaridad.
Puede parecer spam, pero no, este enlace es realmente interesante para espacios de color :)
El artículo de Wikipedia sobre las diferencias de color enumera una serie de espacios de color y métricas de distancia diseñados para concordar con la percepción humana de las distancias de color.
Convierta a La * b * (también conocido simplemente como "Lab", y también verá la referencia a "CIELAB"). Una buena medida rápida de la diferencia de color es
(L1-L2) ^ 2 + (a1-a2) ^ 2 + (b1-b2) ^ 2
Los científicos de color tienen otras medidas más refinadas, que pueden no valer la pena, dependiendo de la precisión necesaria para lo que estás haciendo.
Los valores a y b
representan colores opuestos de forma similar a cómo funcionan los conos, y pueden ser negativos o positivos. Colores neutros: blanco, los grises son a=0
, b=0
. La L
es el brillo definido de una manera particular, desde cero (oscuridad absoluta) hasta lo que sea.
Explicación cruda: >> Dado un color, nuestros ojos distinguen entre dos amplios rangos de longitud de onda: azul versus longitudes de onda más largas. y luego, gracias a una mutación genética más reciente, los conos de longitud de onda más largos se bifurcaron en dos, distinguiéndonos rojo contra verde.
Por cierto, será grandioso que su carrera supere a sus colegas de color de las cavernas que solo conocen "RGB" o "CMYK", que son geniales para los dispositivos pero que aspiran a un trabajo de percepción serio. ¡He trabajado para científicos de imágenes que no sabían nada de esto!
Para leer más divertido sobre la teoría de diferencia de color, intente:
- http://white.stanford.edu/~brian/scielab/introduction.html y la información
- y enlaces sobre teoría del color en general, websurf comenzando con http://www.efg2.com/Lab/Library/Color/ y
- http://www.poynton.com/Poynton-color.html
Más detalles en Lab en http://en.kioskea.net/video/cie-lab.php3. No puedo encontrar una página no fea que tenga las fórmulas de conversión, pero estoy seguro de que alguien editará esta página. respuesta para incluir uno.
como el enlace cmetric.htm anterior falló para mí, así como muchas otras implementaciones para la distancia de color que encontré (después de un jurney muy largo ...) cómo calcular la mejor distancia de color, y ... la más científicamente exacta: deltaE y de 2 Valores RGB (!) Usando OpenCV:
Esto requirió 3 conversiones de espacio de color + alguna conversión de código de javascript ( http://svn.int64.org/viewvc/int64/colors/colors.js ) a C ++
Y finalmente el código (parece funcionar de inmediato, espero que nadie encuentre un error grave allí ... pero parece estar bien después de una serie de pruebas)
#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 );
}
Espero que ayude a alguien :)
HSL y HSV son mejores para la percepción del color humano. De acuerdo con Wikipedia :
A veces es preferible trabajar con materiales artísticos, imágenes digitalizadas u otros medios para usar el modelo de color HSV o HSL sobre modelos alternativos como RGB o CMYK, debido a las diferencias en la forma en que los modelos emulan cómo los humanos perciben el color. RGB y CMYK son modelos aditivos y sustractivos, respectivamente, que modelan la forma en que las luces de colores primarios o los pigmentos (respectivamente) se combinan para formar nuevos colores cuando se mezclan.