Mostrando entradas con la etiqueta Visión Computacional. Mostrar todas las entradas
Mostrando entradas con la etiqueta Visión Computacional. Mostrar todas las entradas

jueves, 16 de mayo de 2013

[VC] Proyecto Final: Detección de señales de tráfico

Presentación




Reporte


Vídeos
Prueba en un ambiente real, sujeto al ruido. No todas las señales se detectan, solo aquellas utilizadas en el entrenamiento, como son el signo de cruce peatonal y parada de autobus


Prueba simple para detectar las señales utilizadas en el entrenamiento.

Código

martes, 23 de abril de 2013

[VC] Tarea 6: Detección de agujeros

Para esta actividad se debieron programar las rutinas para detectar agujeros de diferentes tamaños en una imagen. Para ello se utilizó el método del histograma lateral.

Requisitos

  • Los agujeros detectadas se marcan con un borde morado oscuro y un relleno de morado claro.
  • Un tono ligeramente diferente en cada agujero. Se marca el centro de cada agujero con un punto amarillo.
  • Al centro de cada agujero se agrega una etiqueta del ID del agujero.
  • El programa imprime un listado que indica para cada ID el tamaño del agujero (como porcentajes del tamaño de la imagen).

Teoría


Los pasos necesarios para la detección de agujeros son:
  • Pre-procesar la imagen con filtro en escala de grises.
  • Realizar una sumatoria, por filas y por columnas, para obtener los histogramas horizontal y vertical.
  • Calcular los mínimos locales en los historiales.
  • (Opcional) Normalizar los valores del histograma para que coincidan con las dimensiones de la imagen (para dibujar el histograma sobre la imagen).
  • (Opcional) Guardar los datos del histograma en un archivo para graficarlos posteriormente (para graficar el histograma en otra imagen).
  • Recorrer las listas de mínimos locales horizontales y verticales, y armar pares de coordenadas (intersecciones).
  • Buscar aquellas intersecciones que proporcionan los menores valores al sumarse, por lo general, un agujero tendrá pixeles más obscuros debido a los cambios en la iluminación, entonces, los pixeles más oscuros rodeados de pixeles más claros son indicios de un supuesto agujero en la imagen.
  • Realizar BFS, comenzando en la intersección, para hallar todos los pixeles que pertenecen al agujero, como validación adicional, se puede verificar que el tamaño del objeto encontrado no sea un cierto porcentaje de la imagen , por ejemplo, 5%. En otro caso, posiblemente sea otro tipo de objeto y pueda detectarse utilizando otras técnicas.
  • Colorear los objetos encontrados de acuerdo a las especificaciones, etiquetarlos e imprimir en pantalla su información.

Procedimiento


Principalmente se siguieron los pasos descritos en la parte de arriba, las intersecciones entre 2 mínimos (o máximos) del histograma muestran un posible agujero, basta con verificar si el pixel en esa intersección cumple con un umbral de iluminación para clasificarlo como agujero, después basta con realizar la técnica de BFS en ese lugar para capturar todos los pixeles que forman parte del agujero.
Las imágenes utilizadas para la actividad fueron:

Original
Escala de grises


Resultados


Los resultados del preprocesamiento se muestran en esta entrada





Como vemos en la imagen de la galleta, algunos agujeros son muy pequeños que pasan desapercibidos, pero en las otras imágenes se detectaron bien.

Código


En el repositorio encuentran la implementación completa en código, la carpeta marcada como Tarea 7.



Para dibujar los agujeros


Referencias:
  • N/A

martes, 16 de abril de 2013

[VC] Tarea 5: Detección de elipses

Para esta actividad se debieron programar las rutinas para detectar elipses y círculos de diferentes tamaños en una imagen. Para ello se utilizó la Transformada de Hough y el método de la cuerda-tangente.

Teoría

Los pasos necesarios son:
  • Tomar la imagen original y aplicar detección de bordes, cuidar que los bordes sean lo suficientemente continuos.
  • Elegir las máscaras a aplicar, sigo trabajando con 2 máscaras de Sobel
$\begin{bmatrix}-1 & 0 & 1\\-2 & 0 & 2\\-1 & 0 & 1\end{bmatrix}$ $\begin{bmatrix} 1 & 2 & 1\\  0 & 0 & 0\\ -1 & -2 & -1\end{bmatrix}$
  • Aplicar las máscaras utilizando convolución discreta (2D)
    • Aplicar por separado para obtener 2 gradientes Gx y Gy
  • Recorrer la lista de puntos de los bordes y elegir 2 puntos, aplicar alguna regla para evitar elegir dos puntos muy cercanos; así mismo, aplicar una regla para la selección de los puntos, puede ser al azar o recorrer la lista de pixeles de bordes en orden e ir armando los pares respetando la primer regla.
  • Elegir una gradiente para cada punto de acuerdo a sus coordenadas:
$$ gx = Gx \left [ y, x \right ] $$ $$ gy = Gy \left [ y, x \right ] $$
  • A partir de las matrices de gradientes obtenidas, calcular el ángulo para cada pixel de acuerdo a la formula:
$$ \theta = \arctan \left ( gy/gx \right ) $$ *Nota: Tuve que ajustar el ángulo obtenido, no sé si sea alguna causa local, pero si el dibujo de la recta tangente sale mal orientado primero asegurarse que el ángulo no sea mayor a 2$pi$ o menor a 0.
Después girar el ángulo con el siguiente ajuste:
$$ \theta = \left (\theta - \frac{pi}{2}  \right )(-1) $$
  • Dibujar la recta tangente para cada punto, para mi caso particular utilicé una fórmula donde solo de necesita un punto y el ángulo, así como un largo arbitrario el cual lo definí con la longitud de la diagonal máxima de la imágen.  La fórmula regresa los puntos donde comienza (xA,yA) y finaliza (xB, yB) la línea tangente (que es todo lo que necesitamos), tomando en cuenta que las coordenadas del pixel seleccionado son su coordenadas de origen. La fórmula es:
$$\left ( xA,yA  \right ) = \left ( x + (L \cos \left ( \theta \right ) ), y + (L \sin \left ( \theta \right ) ) \right )$$ $$\left ( xB,yB  \right ) = \left ( x + (L \cos \left ( \theta - \pi \right ) ), y + (L \sin \left ( \theta - \pi \right ) ) \right )$$
  • Identificar si las rectas tangentes se cruzan o no, si es así, detectar el punto de cruce.
  • Calcular el punto medio a partir de los puntos del borde que tocan las rectas tangentes.
  • Dibujar una linea entre el punto de cruce de las lineas tangentes y el punto medio.
  • Alargar dicha linea y someter a votación los pixeles que cruza dicha linea.
  • Agrupar los votos y definir el centro candidato.
  • Calcular los semidiametros del elipse a partir del centro candidato.
  • Aplicar la ecuación del elipse para dibujar el contorno del elipse.

Práctica

Las imagenes utilizadas para las pruebas fueron las siguientes:

Original
Binarizada
Contornos

Para la prueba realice una corrida exhaustiva, buscando casi todas las combinaciones de pares de puntos para comparar las tangentes, respetando los umbrales correspondientes.

Esta es una imágen que muestra un paso intermedio del proceso, las lineas azules son las tangentes, los puntos verdes son el origen de las tangentes, punto medio y punto de cruce de las tangentes, los puntos rojos son los pixeles sometidos a votacion:


La idea es repetir el paso mostrado en la imagen suficientes veces como para que el centro del elipse sea el mas votado.

Resultados


Los resultados de las pruebas fueron los siguientes


Como se puede ver en los resultados, algunas veces los centros de los elipses son detectados lejos del centro real, desplazados; esto afecta también el dibujo de los semidiámetros,
Pienso que falta un ajuste en el método de votación y de agrupación de votos.

Se imprimen en pantalla los ID de los elipses junto con el listado que incluye los tamaños de los semidiametros en pixeles y su porcentaje vs. el tamaño de la diagonal mayor de la imagen.


Código


En el repositorio encuentran la implementación completa en código, la carpeta marcada como Tarea 6.




Referencias:

martes, 5 de marzo de 2013

[VC] Tarea 4: Detección de círculos (radio conocido)


Para ésta tarea se debieron programar las rutinas para detectar circulos con radio conocido y preestablecido un una imágen. Para ello se utilizó la Transformada de Hough.


Para la detección de bordes es necesario:
  • Aplicar máscaras mediante el método de convolución discreta.

Los pasos necesarios son muy parecidos a la detección de lineas, éstos son:
  • Elegir las máscaras a aplicar, en mi caso elegí 2 máscaras de Sobel
  • Aplicar las máscaras utilizando convolución discreta (2D)
    • Aplicar por separado para obtener 2 gradientes Gx y Gy
    • Obtener G con la fórmula
$$ G=\sqrt{Gx^{2} + Gy^{2}} $$
  • A partir de las matrices de gradientes obtenidas, calcular los ángulos para seno y coseno con las fórmulas:
$ \cos\theta = Gx/G $
$ \sin\theta = Gy/G $
  • Hayar las coordenadas del centro del circulo aplicando las formulas
$x_{c} = x - r \cos\theta$
$y_{c} = y - r \sin\theta$
  • Hacer esto por cada pixel para obtener las coordenadas centro a partir de un radio establecido.
  • Declarar un acumulador de votos, que puede ser un arreglo del mismo tamaño de la imágen en pixeles. El acumulador de votos recoge los potenciales centros de circulos de acuerdo al número de votaciónes de los pixeles que lo rodean.
  • Agrupar los votos que ésten demasiado cerca, que el pixel con mayores votos se coma a los pixeles con menores votos para reducir el ruido y centros falsos.
  • Los pixeles con mayores votos son los centros, implementar las rutinas necesarias para descartar falsas alarmas no detectadas en el proceso anterior.
  • A partir de los centros, dibujar el contorno de los círculos encontrados, marcar su centro, etiquetar cada círculo y obtener el diámetro total.

Las imágenes necesitan tratarse antes de pasar por el proceso de detección de círculos:
  • Binarizar la imágen
  • Obtener su contorno
El radio es un parámetro de la simulación y se mide utilizando cualquier software de edición de imágenes, simplemente se mide cuantos pixeles hay de contorno a contorno en el centro, asi obtenemos el diámetro, diámetro entre dos nos da el radio estimado en pixeles.

Las imágenes utilizadas para el ejercicio fueron las siguientes:


Datos Original Binarizada Contornos
Radio=72px
Radio=46px
Radio=41px

Después las imágenes pasan por el proceso donde se detectan los círculos  los contornos se pintan de color amarillo y los centros se marcan con un punto verde y se les coloca una etiqueta. Así mismo, en la terminal, se imprime el ID del círculo y la longitud de su diámetro  el porcentaje del diámetro se calcula a partir de la diagonal máxima de la imagen.

Los resultados fueron


Como podemos ver, los círculos se detectan muy bien, puede haber pocos círculos, muchos círculos o círculos incompletos.
El único bug visible es un centro ligeramente desplazado en la última imágen, pero creo es más cuestión de ajustes de los parámetros de procesamiento que un error en si.


Código

En el repositorio encuentran la implementación completa en código, la carpeta marcada como Tarea 5.

El dibujo en el canvas se hace con la siguiente función

martes, 26 de febrero de 2013

[VC] Tarea 3: Detección de líneas

Para ésta tarea se debieron programar las rutinas para detectar líneas horizontales y verticales un una imágen. Para ello se utilizó la Transformada de Hough.


Para la detección de bordes es necesario:
  • Aplicar máscaras mediante el método de convolución discreta.

Primeramente, los pasos necesarios para la detección son:
  • Elegir las máscaras a aplicar, en mi caso elegí 2 máscaras de Sobel
  • Aplicar las máscaras utilizando convolución discreta (2D)
    • Aplicar por separado para obtener 2 gradientes gx y gy
  • A partir de las matrices de gradientes obtenidas, calcular el ángulo para cada pixel de acuerdo a la formula:
$$ \theta = \arctan \left ( gy/gx \right ) $$
  • Calcular la variable rho aplicando la formula
$$\rho = x \cos \left ( \theta \right ) + y \sin \left ( \theta \right )$$
  • Establecer las condiciones necesarias para obtener los ángulos correctos.
  • Armar pares ($\rho , \theta$) y almacenar dichos pares en una matriz del mismo tamaño (ancho, alto) de la imágen original, a cada pixel le corresponde un par ($\rho , \theta$).
  • Realizar un histograma para cada par ($\rho , \theta$), si un cierto número de pixeles tiene coincidencia en dicho par  ($\rho , \theta$) es probable que existan líneas en la imágen.
  • Clasificar las líneas en horizontales y verticales, colorearlas de diferente tono.

Las máscaras aplicadas fueron obtenidad de la siguiente lectura:


Resultados

Primeramente, estas son las imágenes utilizadas para realizar las pruebas.


Los resultados obtenidos de la aplicación de la transformada fueron:


Los resultados son parciales y no muy favorables, posiblemente se necesitan algunos ajustes en el cálculo de los ángulos y en la realización del histograma.

Código




Referencias

lunes, 18 de febrero de 2013

[VC] Tarea 2: Detección de formas

Para esta entrada el objetivo fue programar las rutinas necesarias para la detección de formas en una imagen.
Con lo que hicimos en la tarea 1, detección de bordes utilizando máscaras de Prewitt y aplicación de umbrales para un mejor resultado:


ahora vamos a detectar los objetos que quedan encerrados dentro de los contornos resaltados en color blanco, para ello probaremos con las siguientes imágenes:


Imágenes originales

Para comenzar necesitamos binarizar las imágenes con la técnica de convolución que ya vimos.
Después de aplicar máscaras y umbrales tendremos algo como ésto:

Imágenes binarizadas

Ahora a detectar los objetos, es un procedimiento sencillo y complejo a la vez:

  • Creamos una matriz de pixeles visitados del mismo tamaño que la imagen (alto x ancho)
  • Creamos una lista para guardar la información de los objetos que encontremos y un contador que servirá para poner un ID a cada objeto.
  • Ahora comenzamos a recorrer todos los pixeles de la imagen.
  • Verificamos cada pixel en el que nos posicionamos en la matriz de pixeles visitados, si no esta marcado como visitado (visitado[x][y] = False) y el color del pixel es negro entonces marcamos ese pixel como el origen de nuestra búsqueda (los pixeles negros pertenecen a objetos, los blancos a bordes).
  • Generamos un color aleatorio
  • Aplicamos búsqueda en anchura BFS en el pixel que marcamos como origen para pintar con el color generado los pixeles que nos lance la búsqueda en anchura.
  • BFS nos regresará la lista de pixeles que fueron visitados, dichos pixeles nos sirven para calcular también la masa del objeto y buscar su centro. Además nos regresa la lista de pixeles pintados de la imágen.
  • Calculamos sus propiedades: 
    • tamaño del objeto a partir del largo de la lista de pixeles visitados
    • su porcentaje con una regla de tres simple utilizando el tamaño del objeto y a cantidad total de pixeles en la imágen.
  • Tomamos las coordenadas de la lista de pixeles visitados y realizamos 2 sumas, en una sumamos los valores de las coordenadas x y en la otra suma los valores de las coordenadas y, después dividimos la sumatoria entre la cantidad de valores sumados. Este par de resultados son las coordenadas del centro de masa del objeto.
  • Tomamos las propiedades y armamos un objeto en forma de diccionario, ahí empaquetamos toda la información.
  • Por cada objeto creado incrementamos el contador de los IDs
  • Repetimos hasta que todos los pixeles hayan sido visitados.
  • Después, de la lista de objetos tomamos aquel de mayor tamaño, supondremos que el objeto de mayor tamaño es el fondo de la imágen por ahora. Pintamos los pixeles que pertenecen a ese objeto de color gris.
  • Ahora solo nos queda recorrer la lista de objetos obteniendo las propiedades de cada uno, mas precisamente, las coordenadas de su centro. En esas coordenadas dibujamos un punto negro y colocamos una etiqueta con el ID del objeto al que pertenece.
  • Mientras en terminal imprimimos sus propiedades para que podamos relacionarlas.


La rutina BFS esta basada en el algoritmo visto en clase.

Los objetos identificados se colorean y se ven así:

Objetos identificados con centro de masa

Podemos ver el centro de masa marcado con un punto negro.


Por último los etiquetamos.

Objetos identificados con centro de masa

Mientras en la terminal vemos la información recolectada de cada objeto, su tamaño en pixeles y porcentaje.



Y así termina la entrada, en el repositorio encuentran la implementación completa en código, la carpeta marcada como Tarea 2.

Código:

Clasificación de objetos parte 1, que hace el trabajo en la interfaz gráfica

Clasificación de objetos parte 2, encargada de la parte lógica

Interfaz


La interfaz permanece sin cambios, al menú Machine vision se agregaron las opciones:

  • Convex hull: Envoltura de objetos, aún sin funcionalidad
  • Object Detection: Detección de un objeto individual
  • Object Classification: Detección de todos los objetos en una imágen

Como agregado hice una rutina llamada objectDetection cuyo objetivo es detectar una sola figura dada las coordenadas de origen.
Coloque un listener en el canvas para guardar las coordenadas donde se da clic, después la rutina toma esas coordenadas como inicio para detectar ese objeto utilizando BFS. No etiqueta ni marca centros de masa, pero es una buena práctica para simular el rellenado de figuras que vemos en programas de dibujo. Las coordenadas seleccionadas se muestran en terminal, la figura se rellena de color azul sólido



Citar el blog de Emmanuel García donde me apoye para optimizar el algoritmo de búsqueda de centros de masa.