Distancias entre colores de LEGO

RGBEn artículos anteriores hemos visto cómo diferenciar colores en una imagen. Por ejemplo, cuando en una imagen teníamos una pelota azul y una roja las podíamos distinguir por sus valores RGB sin mucho problema; en este caso nos valía con que tuviera un valor de rojo o azul alto y bajos los demás para saber si era roja o azul. Sin embargo, cuando tenemos muchos más colores el problema se complica. Por ejemplo, si hay dos tonos de rojo, ¿cómo sabemos si el píxel que acabamos de detectar pertenece a uno o a otro?. El problema se agrava teniendo en cuenta que tenemos que dejar un margen en los valores RGB del color que buscamos, ya que las condiciones de iluminación o la calidad de la foto pueden variar bastante estos valores.

Distancia entre colores:
Todos los colores que usa LEGO tienen un RGB fijo, como podéis ver en el artículo Paleta Oficial de Colores LEGO, por tanto ya contamos con información importante a priori. Si vamos a intentar distinguir entre varios colores es importante saber la distancia entre ellos, cuanto más cerca estén más parecidos serán, y más difíciles de distinguir entre sí. Puesto que tenemos tres valores, la distancia es la misma que la distancia entre dos puntos en un sistema en tres dimensiones. La fórmula para el cálculo de distancia entre dos puntos P1 y P2 de coordenadas (x1, y1, z1) y (x2, y2, z2), respectivamente, es la siguiente:

distancia-3D

En nuestro caso sustituiremos las coordenadas xyz por los valores RGB. Lógicamente si tenemos 10 colores diferentes tendremos muchas distancias, pero a nosotros nos interesa particularmente la mínima distancia existente entre cualquiera de los colores que queremos diferenciar. Es decir, queremos detectar c. Esta distancia mínima nos indica lo cerca que se encuentran los dos colores más parecidos, y podemos crear un valor umbral menor a esa distancia, de manera que no confundamos una pieza con otra cuando la estemos buscando en la imagen.

¿Qué es este valor umbral? se estará preguntando alguno. Si habéis leído mis artículos anteriores de visión artificial (como Reconocimiento básico de objetos con OpenCV) os habréis fijado que para distinguir el rojo por ejemplo no uso un valor RGB fijo, sino que busco todos aquellos que tengan un valor de R mayor a 80 (osea entre 81 y 256). Esto me permite distinguir fácilmente algo rojo, pero sin embargo hubiera dos objetos de rojos distintos ya no valdría, tendría que buscar a cada uno en un margen mucho más pequeño. Por ejemplo si uno tiene un R de 200 y el otro de 130 y he calculado un umbral de 30, buscaré el primero entre los que valgan de 170 a 230, y el segundo en los píxeles que tengan un R de 100 a 160. Por supuesto el umbral tiene en cuenta todos los valores RGB, pero esto sólo era un ejemplo.

En el siguiente diagrama RGB podéis ver la representación aproximada de lo que serían dos colores, sus distancias y su valor umbral que garantiza un área de seguridad a la hora de distinguir colores:

Modelo_RGB

Implementación en C++:

No me voy a alargar mucho en la explicación, ya que tampoco es necesario que sepáis hacer todo esto. Para calcular la distancia mínima entre un grupo de colores de tamaño indefinido he creado dos clases en C++: una es la clase Colores con operaciones básicas para colores, otra es la clase ListaColores que contiene una lista de colores de la longitud que queráis, con toda la funcionalidad necesaria para resolver este problema. Por último un programa de ejemplo para probar estas dos clases.

Las clases en C++ se suelen dividir en dos archivos, uno que es el .h donde definimos la estructura de la clase y que funciones va a tener, y el segundo .cpp donde implementamos las funciones. Por tanto el código sería el siguiente:

Color.h:
//=============================================================
// Nombre : Color.h
// Autor : Álvaro Peláez Santana
// Version : 1.0
// Copyright : www.electricbricks.com
// Descripción : Clase color
//=============================================================

class Color {
private:
int rojo, verde, azul;

public:
void SetRGB(int rojo, int verde, int azul);
int Rojo();
int Verde();
int Azul();
float Distancia(Color color);
};

Color.cpp:
//=============================================================
// Nombre : Color.cpp
// Autor : Álvaro Peláez Santana
// Version : 1.0
// Copyright : www.electricbricks.com
// Descripción : Clase color
//=============================================================

#include "Color.h"
#include

using namespace std;

void Color::SetRGB(int R, int G, int B){
rojo = R;
verde = G;
azul = B;
}

int Color::Rojo(){
return rojo;
}
int Color::Verde(){
return verde;
}

int Color::Azul(){
return azul;
}

float Color::Distancia(Color color2){
float aux;
aux = sqrt(pow((rojo - color2.rojo),2) +
pow((azul - color2.azul),2) +
pow((verde - color2.verde),2));
return aux;
}

ListaColores.h:
//=============================================================
// Nombre : ListaColores.h
// Autor : Álvaro Peláez Santana
// Version : 1.0
// Copyright : www.electricbricks.com
// Descripción : Clase ListaColores
//=============================================================

#include "Color.h"

class ListaColores {
private:
int num_colores, colores_act;
Color colores[100];

public:
void NumColores(int num);
void AnadirColor(Color color1);
float DistanciaMinima(int id_col1, int id_col2);
};

ListaColores.cpp:
//=============================================================
// Nombre : ListaColores.cpp
// Autor : Álvaro Peláez Santana
// Version : 1.0
// Copyright : www.electricbricks.com
// Descripción : Clase ListaColores
//=============================================================

#include "ListaColores.h"
#include
using namespace std;

void ListaColores::NumColores(int num){
num_colores = num;
colores_act = 0;
}
void ListaColores::AnadirColor(Color color1){
if (colores_act < num_colores){ colores[colores_act] = color1; colores_act++; } } float ListaColores::DistanciaMinima(int id_col1, int id_col2){ float min = 4000; for(int i = 0; i < num_colores; i++){ for(int j = i + 1; j < num_colores; j++){ if (colores[i].Distancia(colores[j]) < min){ min = colores[i].Distancia(colores[j]);; id_col1 = i; id_col2 = j; } } } printf("La distancia mínima es de: %6.2f entre el color %d y el color %d", min, id_col1 + 1, id_col2 + 1); return min; }

Y finalmente el programa de prueba, donde vamos a meterle catorce valores diferentes de colores de la Paleta Oficial de Colores LEGO:

//=============================================================
// Nombre : DistanciaColores.cpp
// Autor : Álvaro Peláez Santana
// Version : 1.0
// Copyright : www.electricbricks.com
// Descripción : Cálculo de la distancia mínima entre dos colores
//=============================================================

#include
#include
#include "ListaColores.h"
#include #include

using namespace std;

int main() {
ListaColores lista;
lista.NumColores(16);

/////////Color 1
Color amarillo;
amarillo.SetRGB(254,196,0);
lista.AnadirColor(amarillo);

/////////Color 2
Color rojo;
rojo.SetRGB(222,0,13);
lista.AnadirColor(rojo);

/////////Color 3
Color azul;
azul.SetRGB(0,87,168);
lista.AnadirColor(azul);

/////////Color 4
Color verde;
verde.SetRGB(0,123,40);
lista.AnadirColor(verde);

/////////Color 5
Color negro;
negro.SetRGB(1,1,1);
lista.AnadirColor(negro);

/////////Color 6
Color blanco;
blanco.SetRGB(244,244,244);
lista.AnadirColor(blanco);

/////////Color 7
Color lima;
lima.SetRGB(149,185,11);
lista.AnadirColor(lima);

/////////Color 8
Color tierra;
tierra.SetRGB(217,187,123);
lista.AnadirColor(tierra);

/////////Color 9
Color marron_rojizo;
marron_rojizo.SetRGB(91,28,12);
lista.AnadirColor(marron_rojizo);

/////////Color 10
Color gris_o_azulado;
gris_o_azulado.SetRGB(76,81,86);
lista.AnadirColor(gris_o_azulado);

/////////Color 11
Color gris_c_azulado;
gris_c_azulado.SetRGB(156,146,145);
lista.AnadirColor(gris_c_azulado);

/////////Color 12
Color azul_medio;
azul_medio.SetRGB(71,140,198);
lista.AnadirColor(azul_medio);

/////////Color 13
Color tierra_oscuro;
tierra_oscuro.SetRGB(141,116,82);
lista.AnadirColor(tierra_oscuro);

/////////Color 14
Color rojo_oscuro;
rojo_oscuro.SetRGB(128,8,27);
lista.AnadirColor(rojo_oscuro);

/////////Color 15
Color rosa;
rosa.SetRGB(238,157,195);
lista.AnadirColor(rosa);

/////////Color 16
Color rosa_oscuro;
rosa_oscuro.SetRGB(222,55,139);
lista.AnadirColor(rosa_oscuro);

int col1 = 2, col2 = 2;
lista.DistanciaMinima(col1, col2);

return 0;
}

Lo ejecutamos y nos muestra por consola que dos colores son los más cercanos y cual es su distancia:

Distancia mínima

En este caso los colores 9 y 14 son el marrón rojizo y el rojo oscuro. Espero que os haya interesado el artículo. Toda está información la utilizaremos para posteriores artículos donde identificar las piezas de distinto color en una imagen. Por supuesto si tenéis alguna pregunta podéis escribir en el foro.

Comments are closed.