从霍夫线中选择线条

10

我正在使用Hough Lines用于对这张图像进行角点检测。我打算将线的交点作为拐角点。 这是图片。 enter image description here

不幸的是,Hough返回了许多线条,而非我期望的每条线条。 enter image description here

如何调整Hough Lines参数,使得每个角落只有四条线分别对应图片上的实际线?

5个回答

13

OpenCV的霍夫变换确实需要更好的非极大值抑制。如果没有这个,你会得到重复线的现象。不幸的是,我不知道除了重新实现自己的霍夫变换之外,还有什么容易调整的方法。(这是一个可行的选择,霍夫变换相当简单)

幸运的是,后期处理很容易解决:

对于非概率霍夫变换,OpenCv将按照置信度对线进行排序,首先是最强的线。因此,只需取出在rho或theta上明显差异的前四条线。

  • 所以,将HoughLines找到的第一条线添加到新列表strong_lines
  • 对于每条由HoughLines找到的线:
    • 测试它的rho和theta是否接近任何strong_lines(例如,rho在50像素内,theta在另一条线的10°内)
    • 如果没有,则将其放入strong_lines列表中
    • 如果您找到了4个strong_lines,则退出

10

我实现了HugoRune所描述的方法,并想分享我的代码作为该方法的一个示例。我使用了5度和10像素的容差。

strong_lines = np.zeros([4,1,2])

minLineLength = 2
maxLineGap = 10
lines = cv2.HoughLines(edged,1,np.pi/180,10, minLineLength, maxLineGap)

n2 = 0
for n1 in range(0,len(lines)):
    for rho,theta in lines[n1]:
        if n1 == 0:
            strong_lines[n2] = lines[n1]
            n2 = n2 + 1
        else:
            if rho < 0:
               rho*=-1
               theta-=np.pi
            closeness_rho = np.isclose(rho,strong_lines[0:n2,0,0],atol = 10)
            closeness_theta = np.isclose(theta,strong_lines[0:n2,0,1],atol = np.pi/36)
            closeness = np.all([closeness_rho,closeness_theta],axis=0)
            if not any(closeness) and n2 < 4:
                strong_lines[n2] = lines[n1]
                n2 = n2 + 1

编辑:代码已更新以反映有关负rho值的评论


如果您不对已存储在strong_lines中的行(包括第一行)进行归一化,那么这个错误是否会持续存在呢?无论如何,感谢您提供这段代码,它真的很有帮助! - Nirro
我知道已经有一段时间了,但你能详细解释一下那个“归一化”是如何合理的吗?从我所看到的情况来看,对于完全位于中心对角线左侧的线条来说,没有逻辑上避免负的ρ值的方法。 - d0n.key

2

收集所有线的交点

for (int i = 0; i < lines.size(); i++)
{
    for (int j = i + 1; j < lines.size(); j++)
    {       
        cv::Point2f pt = computeIntersectionOfTwoLine(lines[i], lines[j]);
        if (pt.x >= 0 && pt.y >= 0 && pt.x < image.cols && pt.y < image.rows)
        {
            corners.push_back(pt);
        }
    }
}

您可以在谷歌上搜索算法以查找两条线的交点。 一旦收集到所有的交点,您就可以轻松确定最小值和最大值,从而得到左上角和右下角的点。从这两个点,您可以很容易地得到矩形。
这里是两个链接:Sorting 2d point array to find out four cornershttp://opencv-code.com/tutorials/automatic-perspective-correction-for-quadrilateral-objects/ 可供参考。

第二个链接已经失效。 - Nirro

2
这是一个完整的解决方案,使用 OpenCV 2.4 和 Python 2.7.x 编写。该方法基于这个主题的思想。
方法:检测所有行,假设 Hough 函数首先返回最高排名的线条。过滤那些距离和/或角度分开的线条。
所有 Hough 线的图像:https://i.ibb.co/t3JFncJ/all-lines.jpg 过滤后的线条:https://i.ibb.co/yQLNxXT/filtered-lines.jpg 代码:http://codepad.org/J57oVIzs
"""
Detect the best 4 lines for a rounded rectangle.
"""

import numpy as np
import cv2

input_image = cv2.imread("image.jpg")

def drawLines(img, lines):
    """
    Draw lines on an image
    """
    for line in lines:
        for rho,theta in line:
            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), 1)

input_image_grey = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
edged = input_image_grey

rho = 1 # 1 pixel
theta = 1.0*0.017 # 1 degree
threshold = 100
lines = cv2.HoughLines(edged, rho, theta, threshold)

# Fix negative angles
num_lines = lines.shape[1]
for i in range(0, num_lines):
    line = lines[0,i,:]
    rho = line[0]
    theta = line[1]
    if rho < 0:
        rho *= -1.0
        theta -= np.pi
        line[0] = rho
        line[1] = theta

# Draw all Hough lines in red
img_with_all_lines = np.copy(input_image)
drawLines(img_with_all_lines, lines)
cv2.imshow("Hough lines", img_with_all_lines)
cv2.waitKey()
cv2.imwrite("all_lines.jpg", img_with_all_lines)

# Find 4 lines with unique rho & theta:
num_lines_to_find = 4
filtered_lines = np.zeros([1, num_lines_to_find, 2])

if lines.shape[1] < num_lines_to_find:
    print("ERROR: Not enough lines detected!")

# Save the first line
filtered_lines[0,0,:] = lines[0,0,:]
print("Line 1: rho = %.1f theta = %.3f" % (filtered_lines[0,0,0], filtered_lines[0,0,1]))
idx = 1 # Index to store the next unique line
# Initialize all rows the same
for i in range(1,num_lines_to_find):
    filtered_lines[0,i,:] = filtered_lines[0,0,:]

# Filter the lines
num_lines = lines.shape[1]
for i in range(0, num_lines):
    line = lines[0,i,:]
    rho = line[0]
    theta = line[1]

    # For this line, check which of the existing 4 it is similar to.
    closeness_rho   = np.isclose(rho,   filtered_lines[0,:,0], atol = 10.0) # 10 pixels
    closeness_theta = np.isclose(theta, filtered_lines[0,:,1], atol = np.pi/36.0) # 10 degrees

    similar_rho = np.any(closeness_rho)
    similar_theta = np.any(closeness_theta)
    similar = (similar_rho and similar_theta)

    if not similar:
        print("Found a unique line: %d rho = %.1f theta = %.3f" % (i, rho, theta))
        filtered_lines[0,idx,:] = lines[0,i,:]
        idx += 1
   
    if idx >= num_lines_to_find:
        print("Found %d unique lines!" % (num_lines_to_find))
        break

# Draw filtered lines
img_with_filtered_lines = np.copy(input_image)
drawLines(img_with_filtered_lines, filtered_lines)
cv2.imshow("Filtered lines", img_with_filtered_lines)
cv2.waitKey()
cv2.imwrite("filtered_lines.jpg", img_with_filtered_lines)

enter image description here


1
上述方法(由@HugoRune提出并由@Onamission21实现)是正确的,但存在一些小漏洞。 cv2.HoughLines 可能会返回负的rho和theta值,最高可达pi。例如,请注意线(r0,0)非常接近线(-r0,pi-epsilon),但它们不会在上述接近度测试中被找到。 我只需通过应用rho * = -1,theta-=pi处理负rho即可进行接近程度的计算。

哪个是“上述方法”?你能把它作为评论添加到正确的答案中吗? - Nico Haase
1
我不行。评论需要最低的声望分数,而我还没有达到 :( 我是在提及Onamission21实现的@HugoRune的解决方案。 这个解决方案在原则上是正确的,但却忽略了这个重要的错误。 - Bambi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接