Segmentacion basica OpenCV

Robot_CamaraYa hemos visto un artículo sobre reconocimiento básico de objetos con OpenCV, esta vez queremos ir un paso más allá, siendo capaces de diferenciar dos objetos del mismo color.

Segmentacion basica OpenCV

Este artículo se centra en cómo aislar los objetos o partes de objetos del resto de la imagen. Las razones para hacer esto son obvias. En las cámaras de seguridad, por ejemplo, la cámara suele mirar hacia el mismo fondo aburrido, que no es de ningún interés. Lo que interesa de esa imagen es detectar cuándo las personas o vehículos entran en la escena, o cuándo algo se deja en la escena que no estaba allí antes. Queremos aislar esos acontecimientos e ignorar las interminables horas en que nada está cambiando. Este es un ejemplo de los muchos usos que tiene la segmentación.

Detección de dos objetos iguales del mismo color:
Lo que queremos hacer en este caso es detectar dos objetos distintos del mismo color. En este caso serán dos pelotas rojas que estarán en la misma imagen. Lo que vamos ha hacer no es solo detectarlas, sino colorearlas de distintos colores (como azul y verde), calcular sus puntos medios y hallar la distancia en píxeles entre las dos pelotas. Este es un primer ejemplo de segmentación en el que partimos de un conocimiento previo de la imagen, porque sabemos a priori tanto el color de las bolas que queremos detectar, como el número existente de ellas. Pueden existir casos mucho más complejos, en los que no sabemos el color de los objetos a detectar, su número, etc.

Vimos en el programa anterior cmo detectar un solo objeto de un color, pero en este caso sabemos que hay dos, y que unos píxeles pertenecerán a una pelota, otros píxeles pertenecerán a otra. ¿Cómo saber de qué pelota se trata? Este primer problema de segmentación lo resolveremos de la forma más sencilla. Supongamos que tenemos la siguiente imagen que hemos captado con la cámara LEGO de nuestro NXT.

Segmentacion basica OpenCV: Imagen
Vemos que una pelota está claramente más arriba que la otra. Este hecho lo podemos tener en cuenta para distinguir en qué bola estamos. Normalmente cuando vamos recorriendo una imagen y estamos a mitad de una bola, los píxeles rojos estarán pegados unos a otros o como mucho tendrán unos pocos píxeles no rojos entre medias, pero la distancia entre los píxeles rojos de una bola y otra son bastante apreciables. Por ello sabemos que si cuando acabamos de leer un píxel, el anterior estaba mucho más arriba, es que nos hemos encontrado con una nueva bola. Necesitaremos por tanto variables booleanas para saber en qué bola estamos, así como variables de varios tipos para guardar los datos de cada bola.

Programa en OpenCV:

No voy a escribir el programa entero, sino sólo la parte de reconocimiento de los dos objetos distintos de mismo color. Si queréis saber cómo cargar imágenes, o cómo sacar los datos como altura y anchura de la imagen, podéis revisar mi artículo anterior. El código es el siguiente:

Declaración de las variables que vamos a utilizar:

int y_anterior = 0;

int x1_cont = 0;
int y1_cont = 0;

int x1_total = 0;
int y1_total = 0;

int x2_cont = 0;
int y2_cont = 0;

int x2_total = 0;
int y2_total = 0;

bool bola1 = false;
bool bola2 = false;

Bucle anidado recorriendo y procesando la imagen:

for(i=0;i<altura;i++) for(j=0;j<anchura;j++) {
if ((data[i*anchura_fila+j*canales + 2] > 80) &&
!((data[i*anchura_fila+j*canales + 0] > data[i*anchura_fila+j*canales + 2]/2) ||
(data[i*anchura_fila+j*canales + 1] > data[i*anchura_fila+j*canales + 2]/2))){

if(!bola1 && !bola2){
bola1 = true;
y_anterior = i;
}

printf("Punto rojo en %d, %d, valor: %dn",j,i, data[i*anchura_fila+j*canales+2]);

if (y_anterior + 20 < i){
bola1 = false;
bola2 = true;
}

if (bola1){
printf("Bola1n");
y1_total = y1_total + i;
x1_total = x1_total + j;
y1_cont++;
x1_cont++;
data[i*anchura_fila+j*canales] = 255;
data[i*anchura_fila+j*canales + 1] = 0;
data[i*anchura_fila+j*canales + 2] = 0;
} else if(bola2){
printf("Bola2n");
y2_total = y1_total + i;
x2_total = x1_total + j;
y2_cont++;
x2_cont++;
data[i*anchura_fila+j*canales] = 0;
data[i*anchura_fila+j*canales + 1] = 255;
data[i*anchura_fila+j*canales + 2] = 0;
}
y_anterior = i;
}
}

3 a 5 – Si detectamos un píxel rojo…

7 a 10 – Si no habíamos detectado ninguno antes ponemos que estamos en la primera bola, e inicializamos y_anterior a nuestro número de fila actual.

14 a 17 – Si el píxel que acabamos de detectar está al menos veinte filas por debajo del anterior, deducimos que estamos en una nueva bola (bola2). Este cálculo podría ser más preciso, por ejemplo en vez de basarse solo en el valor de la coordenada y (filas), nos podríamos basar también en el de x (columnas), y hallar la distancia al píxel actual (distancia entre dos puntos) de forma que si supera un valor umbral que hayamos elegido se trata de una nueva bola. Si lo hiciéramos así nos daría igual que las bolas estuvieran separadas a la misma altura, o separadas pero en la misma columna.

21 a 24 – Guardamos los datos tal y como en el artículo anterior para el cálculo de puntos medios.

25 a 27 – Pintamos la primera bola de azul.

34 a 36 – Pintamos la segunda bola de verde.

Cálculo de puntos medios y distancia:

int x1_medio = x1_total / x1_cont;
int y1_medio = y1_total / y1_cont;

printf("Punto medio bola1 en %d, %d, valor: %dn",x1_medio, y1_medio, data[i*anchura_fila+j*canales+2]);

int x2_medio = x2_total / x2_cont;
int y2_medio = y2_total / y2_cont;

printf("Punto medio bola2 en %d, %d, valor: %dn",x2_medio, y2_medio, data[i*anchura_fila+j*canales+1]);

float distancia = sqrt(pow(x1_medio - x2_medio,2) + pow(y1_medio - y2_medio,2));
printf("Distancia entre ambas bolas: %6.2fn", distancia);

11 – Calculamos el punto medio según la formula:

Segmentacion basica OpenCV: Distancia

Y en está imagen podemos ver el resultado de cómo quedarían coloreadas las bolas:

Segmentacion basica OpenCV: Imagen procesada
Ante cualquier duda escribid en el foro, estamos siempre atentos a vuestras preguntas.

Comments are closed.