Hoy aprenderemos a detectar líneas y círculos en una imagen, con la ayuda de una técnica llamada transformada de Hough.
¿Qué es el espacio de Hough?
Antes de empezar a aplicar la transformada de Hough a las imágenes, tenemos que entender qué es un espacio de Hough, y lo aprenderemos con un ejemplo.
Espacio de parámetros
Cuando trabajamos con imágenes, podemos imaginar que la imagen es una matriz 2d sobre unas coordenadas x e y, bajo la cual una línea podría describirse como y = mx + b
Pero en el espacio de parámetros, que llamaremos espacio de Hough, puedo representar esa misma línea como m
frente a b
en su lugar, por lo que la caracterización de una línea en el espacio de la imagen, será un único punto en la posición m-b
en el espacio de Hough.
Pero tenemos un problema, con y = mx + b
, no podemos representar una línea vertical, ya que la pendiente es infinita. Así que necesitamos una mejor forma de parametrización, coordenadas polares (rho y theta).
Espacio de Hough
- rho: describe la distancia de la línea al origen
- theta: describe el ángulo que se aleja de la horizontal
Una observación muy importante, sin embargo, es lo que ocurre cuando tomamos múltiples puntos alrededor de una línea, y los transformamos en nuestro espacio de Hough.
Un solo punto en el espacio de la imagen se traduce en una curva en el espacio de Hough, con la particularidad de que los puntos entre una línea en el espacio de la imagen serán representados por múltiples curvas con un solo punto de contacto.
Y este será nuestro objetivo, encontrar los puntos en los que se cruza un grupo de curvas.
¿Qué es la transformada de Hough?
La transformada de Hough es un método de extracción de características para detectar formas simples como círculos, líneas, etc en una imagen.
La característica «simple» se deriva de la representación de la forma en términos de parámetros. Una forma «simple» estará representada sólo por unos pocos parámetros, por ejemplo una línea puede ser representada por su pendiente e intercepción, o un círculo que puede ser representado por x, y y radio.
En nuestro ejemplo de la línea, una transformada de Hough se encargará de procesar los puntos de la imagen y calcular los valores en el espacio de Hough.
El algoritmo para hacer que la transformación ocurra y posteriormente encontrar las curvas de intersección es un poco complicado, y por lo tanto fuera del alcance de este post. Sin embargo, vamos a echar un vistazo a una implementación de este algoritmo, que forma parte de la biblioteca OpenCV.
Detección de líneas con OpenCV
En OpenCV, la detección de líneas mediante la transformada de Hough se implementa en las funciones HoughLines
y HoughLinesP
(transformada de Hough probabilística). Nos centraremos en esta última.
La función espera los siguientes parámetros:
-
image
: imagen fuente binaria de 8 bits y un solo canal. La imagen puede ser modificada por la función. -
lines
: Vector de salida de líneas. Cada línea está representada por un vector de 4 elementos (x_1, y_1, x_2, y_2) , donde (x_1,y_1) y (x_2, y_2) son los puntos finales de cada segmento de línea detectado. -
rho
: Resolución de distancia del acumulador en píxeles. -
theta
: Resolución del ángulo del acumulador en radianes. -
threshold
: Parámetro de umbral del acumulador. Sólo se devuelven las líneas que obtienen suficientes votos -
minLineLength
: Longitud mínima de la línea. Los segmentos de línea más cortos que eso son rechazados. -
maxLineGap
: Espacio máximo permitido entre puntos de una misma línea para enlazarlos.
¿Demasiado complicado? es más fácil con un ejemplo:
# Read image img = cv2.imread('lanes.jpg', cv2.IMREAD_COLOR)# Convert the image to gray-scalegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Find the edges in the image using canny detectoredges = cv2.Canny(gray, 50, 200)# Detect points that form a linelines = cv2.HoughLinesP(edges, 1, np.pi/180, max_slider, minLineLength=10, maxLineGap=250)# Draw lines on the imagefor line in lines: x1, y1, x2, y2 = line cv2.line(img, (x1, y1), (x2, y2), (255, 0, 0), 3)# Show resultcv2.imshow("Result Image", img)
Y aquí está el resultado:
Es muy importante que realmente utilicemos una imagen sólo de bordes como parámetro para la Transformada de Hough, de lo contrario el algoritmo no funcionará como se pretende.
Detección de círculos usando OpenCV
El proceso es prácticamente el mismo que para las líneas, con la excepción de que esta vez usaremos una función diferente de la librería OpenCV. Utilizaremos ahora HoughCircles
, que acepta los siguientes parámetros:
-
image
: Imagen de entrada de 8 bits, monocanal y en escala de grises. -
circles
: Vector de salida de los círculos encontrados. Cada vector se codifica como un vector de punto flotante de 3 elementos (x, y, radio). -
circle_storage
: En la función C es un almacenamiento de memoria que contendrá la secuencia de salida de los círculos encontrados. -
method
: Método de detección a utilizar. Actualmente, el único método implementado es CV_HOUGH_GRADIENT , que es básicamente 21HT -
dp
: Relación inversa entre la resolución del acumulador y la resolución de la imagen. Por ejemplo, si dp=1 , el acumulador tiene la misma resolución que la imagen de entrada. Si dp=2 , el acumulador tiene la mitad de ancho y alto. -
minDist
: Distancia mínima entre los centros de los círculos detectados. Si el parámetro es demasiado pequeño, pueden detectarse falsamente varios círculos vecinos además de uno verdadero. Si es demasiado grande, algunos círculos pueden pasar desapercibidos. -
param1
: Primer parámetro específico del método. En el caso de CV_HOUGH_GRADIENT , es el umbral más alto de los dos que se pasan al detector de bordes Canny() (el más bajo es dos veces más pequeño). -
param2
: Segundo parámetro específico del método. En el caso de CV_HOUGH_GRADIENT , es el umbral acumulador de los centros de los círculos en la etapa de detección. Cuanto más pequeño sea, más círculos falsos se pueden detectar. Los círculos, correspondientes a los valores más grandes del acumulador, serán devueltos primero. -
minRadius
: Radio mínimo del círculo. -
maxRadius
: Radio máximo del círculo.
Recuerda que los parámetros tienen que ser diferentes, ya que no podemos describir un círculo con la misma parametrización que usamos para las líneas, y en su lugar, tenemos que usar una ecuación como (x - x0)^^2 + (y - y0)^^2 = r^^2
.
Y al código:
# Read image as gray-scaleimg = cv2.imread('circles.png', cv2.IMREAD_COLOR)# Convert to gray-scalegray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Blur the image to reduce noiseimg_blur = cv2.medianBlur(gray, 5)# Apply hough transform on the imagecircles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT, 1, img.shape/64, param1=200, param2=10, minRadius=5, maxRadius=30)# Draw detected circlesif circles is not None: circles = np.uint16(np.around(circles)) for i in circles: # Draw outer circle cv2.circle(img, (i, i), i, (0, 255, 0), 2) # Draw inner circle cv2.circle(img, (i, i), 2, (0, 0, 255), 3)
Nota que comparado con el ejemplo anterior, aquí no estamos aplicando ninguna función de detección de bordes. Esto se debe a que la función HoughCircles
tiene incorporada la detección de bordes.
Y el resultado:
Conclusión
La Transformada de Hough es una técnica excelente para detectar formas simples en las imágenes y tiene varias aplicaciones, que van desde las aplicaciones médicas como el análisis de rayos X, TAC y RMN, hasta los coches de autoconducción. Si estás interesado en saber más sobre el espacio de Hough, te recomiendo que realmente ejecutes el código, pruebes diferentes configuraciones por ti mismo, y que consultes la documentación de OpenCV para obtener información adicional.
¡Gracias por leer!