【问题标题】:Fine Tuning Hough Line function parameters OpenCV微调霍夫线函数参数 OpenCV
【发布时间】:2017-06-09 05:24:01
【问题描述】:

我一直试图在正方形周围画 4 条线,以便获得正方形的顶点。由于准确性,我将采用这种方法,而不是直接使用 Harris 或轮廓方法来查找角点。在opencv的内置函数中使用houghlines我无法获得全长线来获得交点,而且我也得到了太多不相关的线。我想知道是否可以微调参数以获得我的要求?如果是,我该怎么做?我的问题与here. 的问题完全相同,但是即使更改了这些参数,我自己也没有得到这些行。我已附上原始图像以及代码和输出:

原图:

代码:

#include <Windows.h>
#include "opencv2\highgui.hpp"
#include "opencv2\imgproc.hpp"
#include "opencv2/imgcodecs/imgcodecs.hpp"
#include "opencv2/videoio/videoio.hpp"

using namespace cv;
using namespace std;

int main(int argc, const char** argv)
{

    Mat image,src;
    image = imread("c:/pics/output2_1.bmp");
    src = image.clone();
    cvtColor(image, image, CV_BGR2GRAY);
    threshold(image, image, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);

    namedWindow("thresh", WINDOW_NORMAL);
    resizeWindow("thresh", 600, 400);

    imshow("thresh", image);

    cv::Mat edges;

    cv::Canny(image, edges, 0, 255);

    vector<Vec2f> lines;
    HoughLines(edges, lines, 1, CV_PI / 180, 100, 0, 0);
    for (size_t i = 0; i < lines.size(); i++)
    {
        float rho = lines[i][0], theta = lines[i][1];
        Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a*rho, y0 = b*rho;
        pt1.x = cvRound(x0 + 1000 * (-b));
        pt1.y = cvRound(y0 + 1000 * (a));
        pt2.x = cvRound(x0 - 1000 * (-b));
        pt2.y = cvRound(y0 - 1000 * (a));
        line(src, pt1, pt2, Scalar(0, 0, 255), 3, CV_AA);
    }

namedWindow("Edges Structure", WINDOW_NORMAL);
resizeWindow("Edges Structure", 600, 400);

imshow("Edges Structure", src);
waitKey(0);

return(0);
}

输出图像:

更新:此图像上有一个框架,因此我可以通过删除该框架来减少图像边界中不相关的线条,但是我仍然没有得到覆盖正方形的完整线条。

【问题讨论】:

  • 只需外推彼此靠近的线并将它们平均以产生每一边的线。然后使用斜率截距形式y=mx+b 得到每条线的方程,您可以在数学上找到交点。或者,它会慢一点,但您可以使用 HoughLines 而不是 HoughLinesP 来获取图像的完整距离,这将更容易平均,但您仍然需要从 rho 转换为斜率截距形式,theta 形式。
  • @AlexanderReynolds 通过这样做,我错过了准确性,我需要这些线条完美地接触到正方形的完整边缘。
  • 事实上,这样做可以提高准确性。您可以将霍夫变换的阈值设置得更高,因为您将通过图像长度外推线。您的线目前比平均水平更不准确。
  • @AlexanderReynolds 你能否提供一个代码示例来说明如何去做?
  • 由于您有如此明确的边界,您甚至不需要对多行进行平均,您可以设置阈值,以便使用HoughLines 而不是HoughLinesP 在每侧获得一行。获得更好线条的一个简单方法是cv2.dilate 你的边缘图,让边缘增长一点,这样线条就可以获得更多的选票。我会看看我能做什么。

标签: c++ opencv hough-transform


【解决方案1】:

很多方法可以做到这一点,我将只举一个例子。但是,我在python 中最快,所以我的代码示例将使用该语言。不过,翻译起来应该不难(在您为他人完成后,请随时使用您的 C++ 解决方案编辑您的帖子)。

对于预处理,我强烈建议dilate()ing 你的边缘图像。这将使线条更粗,这将有助于更好地拟合霍夫线。霍夫线函数在抽象中所做的基本上是制作一个穿过大量角度和距离的线网格,如果这些线越过 Canny 的任何白色像素,那么它会为该线给出它通过的每个点的分数.但是,来自 Canny 的线条不会完全笔直,因此您会得到一些不同的线条得分。使这些 Canny 线条更粗将意味着每条 真正 接近拟合良好的线条将有更好的机会获得更高的得分。

如果您要使用HoughLinesP,那么您的输出将是行segments,您所拥有的只是在线上的两个点。

由于线条大多是垂直和水平的,您可以轻松地根据它们的位置分割线条。如果一条线的两个 y 坐标彼此靠近,则该线大部分是水平的。如果两个 x 坐标彼此靠近,则该线大部分是垂直的。因此,您可以通过这种方式将线条分割成垂直线和水平线。

def segment_lines(lines, delta):
    h_lines = []
    v_lines = []
    for line in lines:
        for x1, y1, x2, y2 in line:
            if abs(x2-x1) < delta: # x-values are near; line is vertical
                v_lines.append(line)
            elif abs(y2-y1) < delta: # y-values are near; line is horizontal
                h_lines.append(line)
    return h_lines, v_lines

然后,您可以从它们的端点using determinants 获取两条线段的交点。

def find_intersection(line1, line2):
    # extract points
    x1, y1, x2, y2 = line1[0]
    x3, y3, x4, y4 = line2[0]
    # compute determinant
    Px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4))/  \
        ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    Py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4))/  \
        ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    return Px, Py

所以现在如果你遍历你的所有线,你会有来自所有水平线和垂直线的交点,但是你有 很多 线,所以你会有很多交点盒子的同一个角落。

但是,这些都在一个向量中,因此您不仅需要平均每个角的点,还需要将它们实际组合在一起。您可以使用 k-means 聚类来实现这一点,它在 OpenCV 中实现为 kmeans()

def cluster_points(points, nclusters):
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    _, _, centers = cv2.kmeans(points, nclusters, None, criteria, 10, cv2.KMEANS_PP_CENTERS)
    return centers

最后,我们可以使用circle() 简单地将这些中心(确保我们先四舍五入——因为到目前为止一切都是浮点数)绘制到图像上,以确保我们做对了。

我们拥有它;四个点,在盒子的角落。

这是我在 python 中的完整代码,包括生成上图的代码:

import cv2
import numpy as np 

def find_intersection(line1, line2):
    # extract points
    x1, y1, x2, y2 = line1[0]
    x3, y3, x4, y4 = line2[0]
    # compute determinant
    Px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4))/  \
        ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    Py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4))/  \
        ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    return Px, Py

def segment_lines(lines, delta):
    h_lines = []
    v_lines = []
    for line in lines:
        for x1, y1, x2, y2 in line:
            if abs(x2-x1) < delta: # x-values are near; line is vertical
                v_lines.append(line)
            elif abs(y2-y1) < delta: # y-values are near; line is horizontal
                h_lines.append(line)
    return h_lines, v_lines

def cluster_points(points, nclusters):
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    _, _, centers = cv2.kmeans(points, nclusters, None, criteria, 10, cv2.KMEANS_PP_CENTERS)
    return centers

img = cv2.imread('image.png')

# preprocessing
img = cv2.resize(img, None, fx=.5, fy=.5)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
dilated = cv2.dilate(edges, np.ones((3,3), dtype=np.uint8))

cv2.imshow("Dilated", dilated)
cv2.waitKey(0)
cv2.imwrite('dilated.png', dilated)

# run the Hough transform
lines = cv2.HoughLinesP(dilated, rho=1, theta=np.pi/180, threshold=100, maxLineGap=20, minLineLength=50)

# segment the lines
delta = 10
h_lines, v_lines = segment_lines(lines, delta)

# draw the segmented lines
houghimg = img.copy()
for line in h_lines:
    for x1, y1, x2, y2 in line:
        color = [0,0,255] # color hoz lines red
        cv2.line(houghimg, (x1, y1), (x2, y2), color=color, thickness=1)
for line in v_lines:
    for x1, y1, x2, y2 in line:
        color = [255,0,0] # color vert lines blue
        cv2.line(houghimg, (x1, y1), (x2, y2), color=color, thickness=1)

cv2.imshow("Segmented Hough Lines", houghimg)
cv2.waitKey(0)
cv2.imwrite('hough.png', houghimg)

# find the line intersection points
Px = []
Py = []
for h_line in h_lines:
    for v_line in v_lines:
        px, py = find_intersection(h_line, v_line)
        Px.append(px)
        Py.append(py)

# draw the intersection points
intersectsimg = img.copy()
for cx, cy in zip(Px, Py):
    cx = np.round(cx).astype(int)
    cy = np.round(cy).astype(int)
    color = np.random.randint(0,255,3).tolist() # random colors
    cv2.circle(intersectsimg, (cx, cy), radius=2, color=color, thickness=-1) # -1: filled circle

cv2.imshow("Intersections", intersectsimg)
cv2.waitKey(0)
cv2.imwrite('intersections.png', intersectsimg)

# use clustering to find the centers of the data clusters
P = np.float32(np.column_stack((Px, Py)))
nclusters = 4
centers = cluster_points(P, nclusters)
print(centers)

# draw the center of the clusters
for cx, cy in centers:
    cx = np.round(cx).astype(int)
    cy = np.round(cy).astype(int)
    cv2.circle(img, (cx, cy), radius=4, color=[0,0,255], thickness=-1) # -1: filled circle

cv2.imshow("Center of intersection clusters", img)
cv2.waitKey(0)
cv2.imwrite('corners.png', img)

最后,只有一个问题……为什么不将 OpenCV 中实现的Harris corner detector 用作cornerHarris()?因为它用非常少的代码就能很好地工作。我对灰度图像进行了阈值处理,然后进行了一点模糊以去除虚假的角落,然后,嗯...

这是使用以下代码生成的:

import cv2
import numpy as np

img = cv2.imread('image.png')

# preprocessing
img = cv2.resize(img, None, fx=.5, fy=.5)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
r, gray = cv2.threshold(gray, 120, 255, type=cv2.THRESH_BINARY)
gray = cv2.GaussianBlur(gray, (3,3), 3)

# run harris
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)

# dilate the corner points for marking
dst = cv2.dilate(dst,None)
dst = cv2.dilate(dst,None)

# threshold
img[dst>0.01*dst.max()]=[0,0,255]

cv2.imshow('dst',img)
cv2.waitKey(0)
cv2.imwrite('harris.png', img)

我认为通过一些小的调整,Harris 角点检测器可能比外推霍夫线交点更准确。

【讨论】:

  • 哈里斯很随意。我需要为其选择正确的参数以返回 4 个一致的点。我不热衷于使用 Harris 的原因是因为我需要检测其他形状的顶点,所以它并不总是有效。我正在尝试以微米为单位测量长度,因此我需要高精度。
  • 在你的函数 def segment_lines(lines, delta): 中,什么是 delta?
  • 变量delta 只是我让xy 在说它们不在同一行之前有所不同。我以delta=10 为例。这种分割假设您的线条几乎是水平或垂直的。如果你的线可以倾斜,那么你会想要使用斜率。但是如果你的线条几乎是垂直的,你就不得不担心一个爆炸或不存在的斜率,在这种情况下,你会想要像霍夫变换一样使用rho, theta 形式。
  • 我也理解准确性的必要性,但我的观点是霍夫一点也不准确。这是一个巨大的近似步骤,尤其是使用HoughLinesP。定义准确的角也很重要。是框内的像素,还是框外的像素?如果角落的像素更灰,介于黑色和白色之间,那是角落还是角落旁边的黑色或白色像素?如果您发布一个新问题,其中包含您实际需要的内容以及对它的期望,您可能会更幸运地找到一个好方法。
  • 聚类后没有得到任何角点的原因是什么?我正在尝试实现您的代码..它给出了很多圆圈,但在聚类后它返回 None 并将其应用于矩形的透视投影。
猜你喜欢
  • 1970-01-01
  • 2012-07-06
  • 2015-02-16
  • 2011-12-17
  • 2020-05-23
  • 2016-02-06
  • 1970-01-01
  • 1970-01-01
  • 2014-05-12
相关资源
最近更新 更多