algorithm - modifyvars - “Redondeando” los valores de color al más cercano de un pequeño conjunto de colores
less parent selector (4)
Dependiendo de la cantidad de colores que esté buscando, ¿por qué no intenta usar operadores bitwise (la referencia de PHP here , ya que lo mencionó en la pregunta) para reducir la cantidad de dígitos significativos? Podría redondear los valores RGB antes de cambiar para aumentar la precisión también.
Preámbulo
Como parte de un proyecto en el que estoy trabajando, estoy tratando de proporcionar una manera conveniente de buscar imágenes en nuestro sistema. Actualmente proporcionamos búsquedas por varios tipos de metadatos agregados por el usuario (por ejemplo, título, descripción, palabras clave) y por varios metadatos que extraemos (por ejemplo, EXIF, IPTC, XMP, etc.). También me gustaría agregar una "búsqueda de color" similar a la que se puede ver en la búsqueda de imágenes de google.
El proyecto utiliza PHP y podemos usar la extensión Imagemagick para segmentar y cuantificar la imagen y extraer los colores más "significativos" de la imagen; No estoy completamente seguro de los resultados que estoy obteniendo aquí, pero parecen razonablemente precisos y ciertamente mejores que nada.
El problema
Lo poco que me cuesta es convertir estos colores significativos en un conjunto de colores representativos, por ejemplo, cuando miras la búsqueda de imágenes de Google, hay un conjunto de 12 colores allí. Me gustaría "redondear" matemáticamente mi valor de color al color representativo más cercano, para poder indexar la imagen con los colores que detecto y luego facetar los resultados de mi búsqueda de esa manera.
¿Alguna sugerencia?
El primer paso sería definir los colores que desea comparar.
El segundo paso es encontrar la distancia más pequeña desde su color hasta uno de los colores que eligió en el paso anterior. Para poder medir esa distancia necesita un espacio euclidiano en el que modelar colores.
Por supuesto, la elección simple sería el espacio RGB.
Y la distancia entre dos colores C 1 (r 1 , g 1 , b 1 ) y C 2 (r 2 , g 2 , b 2 ) sería
sqrt ((r 1 - r 2 ) 2 + (g 1 - g 2 ) 2 + (b 1 - b 2 ) 2 ) .
Pero si necesita más precisión, sería mejor usar el espacio bicono Hue-Chroma-Lightness, un derivado del cilindro HSL.
En el espacio RGB, las cosas eran rectas como R, G y B, donde cada una estaba en un eje separado. En HCL necesitamos calcular las coordenadas en cada uno de los ejes.
En primer lugar, calculamos el chroma (que es un poco diferente de la saturación) como:
Chroma = máx (rojo, verde, azul) - min (rojo, verde, azul)
Luego, normalizamos nuestro valor H, C y L, de modo que H va de 0 a 2 (para cubrir un círculo si multiplicamos por PI y tomamos radianes como la unidad), C va de 0 a 1 (el radio del círculo trigonométrico) y L va de -1 (Negro) a 1 (Blanco).
A continuación, tomamos z = L sin ninguna transformación, ya que se desprende de la imagen que va a lo largo del eje vertical.
Podemos observar fácilmente que para un color, Chroma es la distancia desde el eje z y el tono es el ángulo. Así obtenemos
x = C * cos (H * PI) y
y = C * sin (H * PI)
En este punto, x, y y z estarán todos en [-1, 1] y la distancia entre dos colores será, usando la misma fórmula que la anterior,
sqrt ((x 1 - x 2 ) 2 + (y 1 - y 2 ) 2 + (z 1 - z 2 ) 2 ) .
Para obtener aún más precisión y encontrar el color más cercano según la percepción del color humano, puede utilizar el espacio de modelado CIE-L*ab y calcular la distancia con uno de estos algoritmos . Los principios son los mismos que para los dos casos presentados anteriormente, solo los algoritmos son más complejos.
Actualización (7 años después)
Finalmente, xkcd presentó un cómic que puedo usar en esta publicación.
Esta es solo una idea aproximada: deberá ajustarla a sus propias necesidades.
Básicamente, pensé que, como los colores se registran como RGB, ya sea como una cadena Hex "# 000000" a "#ffffff", o como un conjunto RGB "rgb (0,0,0)" a "rgb (255,255,255)" , y estos son intercambiables / traducibles, esto es un simple problema de redondeo matemático.
En la gama completa de colores habría (16 * 16) * (16 * 16) * (16 * 16) = 256 * 256 * 256 = 16,777,216 colores posibles.
El redondeo de los colores a su carácter más cercano, el valor hexadecimal reduce a 16 * 16 * 16 = 4,096 colores posibles. Todavía demasiados, pero cada vez más cerca.
Redondear los colores a un solo valor de carácter, pero luego limitarlo a uno de 4 (0,3,7, f) lo reduce a 4 * 4 * 4 = 32. Lo suficientemente cerca para mí.
Entonces, construí una función PHP muy básica para tratar de lograr esto:
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> > <span style="background-color:#%s;padding:0 10px;"></span>%s has been rounded to %s<br>'' ,
$incolor , $outR.$outG.$outB ,
$incolor , $outR.$outG.$outB );
}
Esta función, cuando se pasa una cadena hexadecimal, muestra una muestra del color original y una muestra del color abreviado.
Esto es solo una prueba de concepto básica, ya que no sé el formato en el que Imagemagick está devolviendo los colores, pero es posible que pueda utilizar esta lógica para crear la suya propia.
Entonces, a partir de esos 32 colores, podría agrupar los similares (probablemente habría alrededor de 8 tonos de gris) y nombrar el resto para que los usuarios puedan buscarlos.
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];
}