从紧密耦合的线和噪声曲线中找到直线

Ely*_*ium 6 python opencv numpy hough-transform

我有这张林线作物的图像。我需要找到作物对齐的大致方向。我试图获取图像的霍夫线,然后找到角度分布的模式。树木作物线

我一直在关注关于裁剪线的教程,但是在该教程中,裁剪线很稀疏。这里它们是密集的,经过灰度、模糊和使用精明的边缘检测后,这就是我得到的

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()
Run Code Online (Sandbox Code Playgroud)

(左侧未经canny处理的模糊图像,左侧经过canny预处理的图像) 预处理图像

之后,我按照教程将预处理后的图像“骨架化”

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
Run Code Online (Sandbox Code Playgroud)

给我

骨架化的

正如你所看到的,仍然有一堆曲线。当对其使用HoughLines算法时,有11k条线分散在各处

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()
Run Code Online (Sandbox Code Playgroud)

霍夫线

我是 cv2 的新手,所以我不知道该怎么做。搜索并尝试了很多东西但没有任何效果。如何去除稍大的点并去除波浪线?

Jér*_*ard 4

您可以使用2D FFT来查找裁剪对齐的大致方向(如 mozway 在评论中提出的)。这个想法是,当输入包含同一方向的许多线时,可以轻松地从震级谱中出现的中心射束中提取总体方向。您可以在上一篇文章中找到有关其工作原理的更多信息。它直接处理输入图像,但最好应用高斯 + Canny 滤波器。

这是滤波后的灰度图像的幅度谱的有趣部分:

震级谱

可以很容易地看到主光束。您可以通过迭代许多角度不断增加的线来提取其角度,并对每条线上的幅度值求和,如下图所示:

线

以下是根据线的角度(以弧度为单位)绘制的每条线的幅值总和:

相对于角度绘制的总和

在此基础上,您只需要找到使计算总和最大化的角度即可。

这是生成的代码:

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)
Run Code Online (Sandbox Code Playgroud)