es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Calcular el ángulo entre dos objetos en una imagen.

Tengo algunas imágenes específicas de dos objetos (un teléfono y un control remoto de TV) y quiero calcular el ángulo entre dos bordes que se intersecan. Usé Canny para detectar los bordes y la función hough_line para el ángulo, pero la función hough_line encontró demasiados ángulos que no cumplen con el requisito.
Imagen original:
imagen

Este es el requisito:
imagen

Y esto es lo que hice:
imagen

Mi código:

import cv2
from skimage.transform import hough_line, hough_line_peaks
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

def edge_detection(img, blur_ksize=5, threshold1=100, threshold2=200):
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img_gaussian = cv2.GaussianBlur(gray, (blur_ksize, blur_ksize), 0)
    img_canny = cv2.Canny(img_gaussian, threshold1, threshold2)

    return img_canny

image = edge_detection(cv2.imread('img1.png'))

h, theta, d = hough_line(image)
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
ax = axes.ravel()

ax[0].imshow(image)
ax[0].set_title('Imagen original')
ax[0].set_axis_off()

ax[1].imshow(image, cmap=cm.gray)
for _, angle, dist in zip(*hough_line_peaks(h, theta, d)):
    y0 = (dist - 0 * np.cos(angle)) / np.sin(angle)
    y1 = (dist - image.shape[1] * np.cos(angle)) / np.sin(angle)
    ax[1].plot((0, image.shape[1]), (y0, y1), '-r')
ax[1].set_xlim((0, image.shape[1]))
ax[1].set_ylim((image.shape[0], 0))
ax[1].set_axis_off()
ax[1].set_title('Líneas detectadas')

plt.tight_layout()
plt.show()

angle = []
dist = []
for _, a, d in zip(*hough_line_peaks(h, theta, d)):
    angle.append(a)
    dist.append(d)

angle = [a*180/np.pi for a in angle]
print(angle)

¿Hay alguna forma de detectar y calcular exactamente el ángulo que necesito en OpenCV? Muchas gracias.

Actualización:
Intenté diferentes valores de blur_ksize, threshold1 y threshold2 en la detección de Canny, parece que pude eliminar las líneas redundantes, pero ahora los ángulos que hough_line_peaks() devuelve son negativos. ¿Alguien puede explicarme esto? También quiero agregar los valores de los ángulos a los picos en la gráfica, para ver qué ángulo tiene qué valor.
imagen

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Sí, existen diferentes formas de detectar y calcular exactamente un ángulo en OpenCV. Un enfoque podría ser utilizar la función HoughLinesP en lugar de la función HoughLines. HoughLinesP es una transformada de Hough probabilística que puede detectar segmentos de líneas en lugar de líneas completas. Con segmentos de líneas, se pueden calcular los ángulos entre ellos de manera más precisa.

    Aquí hay una versión actualizada de tu código que utiliza HoughLinesP y calcula el ángulo entre los dos segmentos de líneas detectados:

    import cv2
    import numpy as np
    
    
    def deteccion_bordes(img, tamano_desenfoque=5, umbral1=100, umbral2=200):
        gris = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img_gaussiana = cv2.GaussianBlur(gris, (tamano_desenfoque, tamano_desenfoque), 0)
        img_canny = cv2.Canny(img_gaussiana, umbral1, umbral2)
    
        return img_canny
    
    
    img = cv2.imread('img1.png')
    bordes = deteccion_bordes(img)
    
    longitud_minima_linea = 100
    espacio_maximo_entre_lineas = 10
    lineas = cv2.HoughLinesP(bordes, 1, np.pi/180, 100, longitud_minima_linea, espacio_maximo_entre_lineas)
    
    if lineas is not None and len(lineas) >= 2:
        x1, y1, x2, y2 = lineas[0][0]
        longitud_linea1 = np.sqrt((x2-x1)**2 + (y2-y1)**2)
        x1, y1, x2, y2 = lineas[1][0]
        longitud_linea2 = np.sqrt((x2-x1)**2 + (y2-y1)**2)
    
        producto_escalar = lineas[0][0][:2] * lineas[1][0][:2]
        angulo = np.arccos(producto_escalar / (longitud_linea1 * longitud_linea2)) * 180 / np.pi
        print(angulo)
    
        cv2.line(img, (lineas[0][0][0], lineas[0][0][1]), (lineas[0][0][2], lineas[0][0][3]), (0,0,255), 2)
        cv2.line(img, (lineas[1][0][0], lineas[1][0][1]), (lineas[1][0][2], lineas[1][0][3]), (0,0,255), 2)
    
    cv2.imshow("Resultado", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    En este código, primero utilizamos HoughLinesP para detectar segmentos de líneas en la imagen. Luego calculamos la longitud de cada segmento de línea, el producto escalar entre ellos y finalmente el ángulo entre ellos. También dibujamos los dos segmentos de líneas detectados en la imagen original.

    Ten en cuenta que HoughLinesP requiere algunos parámetros adicionales en comparación con HoughLines, incluyendo la longitud mínima de un segmento de línea (longitud_minima_linea) y la distancia máxima entre dos segmentos para considerarlos una sola línea (espacio_maximo_entre_lineas). Es posible que debas ajustar estos parámetros según las imágenes específicas con las que estés trabajando.

    Respecto a los ángulos negativos que estás obteniendo, esto se debe a que la función arccos de NumPy devuelve ángulos en el rango [0, pi] radianes, lo cual corresponde a [0, 180] grados. Para obtener el rango completo de ángulos en grados, debes agregar una condición para verificar si el producto escalar entre los dos segmentos de línea es positivo o negativo, y ajustar el ángulo en consecuencia:

    if producto_escalar < 0:
        angulo = 360 - angulo
    

    Respecto a colocar los valores de los ángulos en los picos del gráfico, una forma de hacerlo es utilizando la función text de Matplotlib, de la siguiente manera:

    for _, angulo, distancia in zip(*hough_line_peaks(h, theta, d)):
        y0 = (distancia - 0 * np.cos(angulo)) / np.sin(angulo)
        y1 = (distancia - imagen.shape[1] * np.cos(angulo)) / np.sin(angulo)
        ax[1].plot((0, imagen.shape[1]), (y0, y1), '-r')
        ax[1].text(10, y0+10, f'{angulo*180/np.pi:.1f} grados', color='red')
    

    Esto colocará los valores de los ángulos encima de cada segmento de línea en el gráfico. Puedes ajustar la posición del texto cambiando las coordenadas x e y, y otras propiedades del texto (como el tamaño de fuente) utilizando argumentos opcionales de text.

Comments are closed.