我一直试图在广场周围找到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);
}
输出图片:
更新:此图片上有一个框架,因此我可以通过移除该框架来减少图像边框中不相关的线条,但是我仍然没有完整的线条覆盖广场。
答案 0 :(得分:13)
有许多方法可以做到这一点,我将举一个例子。但是,我在python
中最快,所以我的代码示例将使用该语言。不过难以翻译它(在你为其他人完成后,请随意用你的C ++解决方案编辑你的帖子。)
对于预处理,我强烈建议dilate()
你的边缘图像。这将使更粗的线条有助于更好地适应Hough线条。霍夫线在抽象中的功能基本上是通过大量的角度和距离来制作线条网格,如果线条超过了Canny的任何白色像素,那么它为该线条提供了每个点的得分。 。然而,来自Canny的线条并不是完全笔直的,所以你会得到一些不同的线路得分。使那些Canny线变粗将意味着真正接近拟合的每条线将有更好的得分机会。
如果您要使用HoughLinesP
,那么您的输出将是段行,其中您只有两个点就行了。
由于线条大多是垂直和水平线,因此您可以根据线条的位置轻松分割线条。如果一条线的两个 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角点探测器可能比推断Hough线交叉点更准确。