Tengo esta imagen para un cultivo de línea de árboles. Necesito encontrar la dirección general en la que se alinea el recorte. Estoy tratando de obtener las líneas de Hough de la imagen y luego encontrar el modo de distribución de los ángulos.
He estado siguiendo este tutorial sobre líneas de recorte, sin embargo, en ese, las líneas de recorte son escasas. Aquí están densamente empaquetados, y después de la escala de grises, el desenfoque y el uso de la detección de bordes astutos, esto es lo que obtengo
import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('drive/MyDrive/tree/sample.jpg') gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) gauss = cv2.GaussianBlur(gray, (3,3), 3) plt.figure(figsize=(15,15)) plt.subplot(1,2,1) plt.imshow(gauss) gscale = cv2.Canny(gauss, 80, 140) plt.subplot(1,2,2) plt.imshow(gscale) plt.show()
(Imagen borrosa del lado izquierdo sin canny, izquierda preprocesada con canny)
Después de eso, seguí el tutorial y "esqueleticé" la imagen preprocesada.
size = np.size(gscale) skel = np.zeros(gscale.shape, np.uint8) ret, gscale = cv2.threshold(gscale, 128, 255,0) element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3)) done = False while not done: eroded = cv2.erode(gscale, element) temp = cv2.dilate(eroded, element) temp = cv2.subtract(gscale, temp) skel = cv2.bitwise_or(skel, temp) gscale = eroded.copy() zeros = size - cv2.countNonZero(gscale) if zeros==size: done = True
Dando me
Como puede ver, todavía hay un montón de líneas curvas. Cuando se usa el algoritmo HoughLines, hay 11k líneas dispersas por todas partes
lines = cv2.HoughLinesP(skel,1,np.pi/180,130) a,b,c = lines.shape for i in range(a): rho = lines[i][0][0] theta = lines[i][0][1] a = np.cos(theta) b = np.sin(theta) x0 = a*rho y0 = b*rho x1 = int(x0 + 1000*(-b)) y1 = int(y0 + 1000*(a)) x2 = int(x0 - 1000*(-b)) y2 = int(y0 - 1000*(a)) cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2, cv2.LINE_AA)#showing the results: plt.figure(figsize=(15,15)) plt.subplot(121)#OpenCV reads images as BGR, this corrects so it is displayed as RGB plt.plot() plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) plt.title('Row Detection') plt.xticks([]) plt.yticks([]) plt.subplot(122) plt.plot() plt.imshow(skel,cmap='gray') plt.title('Skeletal Image') plt.xticks([]) plt.yticks([]) plt.show()
Soy un novato cuando se trata de cv2, así que no tengo ni idea de qué hacer. Busqué y probé un montón de cosas pero ninguna funciona. ¿Cómo puedo eliminar los puntos levemente grandes y eliminar las líneas onduladas?
Puede usar una FFT 2D para encontrar la dirección general en la que se alinea el recorte (como lo propone mozway en los comentarios). La idea es que la dirección general se pueda extraer fácilmente de los rayos de luz centrados que aparecen en el espectro de magnitud cuando la entrada contiene muchas líneas en la misma dirección. Puedes encontrar más información sobre su funcionamiento en este post anterior . Funciona directamente con la imagen de entrada, pero es mejor aplicar los filtros Gaussian + Canny.
Aquí está la parte interesante del espectro de magnitud de la imagen gris filtrada:
El rayo principal se puede ver fácilmente. Puede extraer su ángulo iterando sobre muchas líneas con un ángulo creciente y sumando los valores de magnitud en cada línea como en la siguiente figura:
Aquí está la suma de la magnitud de cada línea trazada contra el ángulo (en radianes) de la línea:
Basado en eso, solo necesita encontrar el ángulo que maximiza la suma calculada.
Aquí está el código resultante:
def computeAngle(arr): # Naive inefficient algorithm n, m = arr.shape yCenter, xCenter = (n-1, m//2-1) lineLen = m//2-2 sMax = 0.0 bestAngle = np.nan for angle in np.arange(0, math.pi, math.pi/300): i = np.arange(lineLen) y, x = (np.sin(angle) * i + 0.5).astype(np.int_), (np.cos(angle) * i + 0.5).astype(np.int_) s = np.sum(arr[yCenter-y, xCenter+x]) if s > sMax: bestAngle = angle sMax = s return bestAngle # Load the image in gray img = cv2.imread('lines.jpg') gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) # Apply some filters gauss = cv2.GaussianBlur(gray, (3,3), 3) gscale = cv2.Canny(gauss, 80, 140) # Compute the 2D FFT of real values freqs = np.fft.rfft2(gscale) # Shift the frequencies (centering) and select the low frequencies upperPart = freqs[:freqs.shape[0]//4,:freqs.shape[1]//2] lowerPart = freqs[-freqs.shape[0]//4:,:freqs.shape[1]//2] filteredFreqs = np.vstack((lowerPart, upperPart)) # Compute the magnitude spectrum magnitude = np.log(np.abs(filteredFreqs)) # Correct the angle magnitude = np.rot90(magnitude).copy() # Find the major angle bestAngle = computeAngle(magnitude)