查找矩形的角落

3
我正在尝试获取这个矩形的角落: this rectangle
我尝试使用 cv2.cornerHarris(rectangle, 2, 3, 0.04),但由于图像亮度问题,左边缘没有显示。因此,在使用cornerHarris之前,我尝试应用阈值,但生成的图像显示沿着边缘有很多顶点,无法过滤角落。
我知道在使用cornerHarris之前需要过滤它,但我不知道如何做。能否有人帮我解决这个问题?
附注:我已经尝试使用模糊处理,但也不起作用。
import cv2
import numpy as np
import matplotlib.pyplot as plt

rectangle = cv2.imread('rectangle.png', cv2.IMREAD_GRAYSCALE)
rectangle = np.where(rectangle > np.mean(rectangle), 255, 0).astype(np.uint8)

dst_rectangle = cv2.cornerHarris(rectangle, 2, 3, 0.04)
dst_rectangle = cv2.dilate(dst_rectangle, None)

mask = np.where(dst_rectangle > 0.01*np.max(dst_rectangle), 255, 0).astype(np.uint8)
points = np.nonzero(mask)

plt.imshow(dst_rectangle, cmap='gray')
plt.plot(points[1], points[0], 'or')
plt.show()

你能分享一个 [mcve] 吗?我不确定这是否适合在 Stack Overflow 上发布。请参见:[ask],[help/on-topic]。 - AMC
我以为描述我所做的就足够了。下次我会分享可重现的代码。感谢建议。 - ar018
为什么只能下一次编辑呢?你仍然可以,并且应该编辑你的帖子,不是吗? - AMC
完成。因为问题已经被解决了。 - ar018
4个回答

8
我会通过获取自适应阈值处理后轮廓的旋转边界框的角来采用不同的方法。以下是我的Python/OpenCV代码。
输入: enter image description here
import cv2
import numpy as np

# read image
img = cv2.imread("rectangle.png")

# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = 255-gray

# do adaptive threshold on gray image
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 17, 1)
thresh = 255-thresh

# apply morphology
kernel = np.ones((3,3), np.uint8)
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)

# separate horizontal and vertical lines to filter out spots outside the rectangle
kernel = np.ones((7,3), np.uint8)
vert = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)
kernel = np.ones((3,7), np.uint8)
horiz = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)

# combine
rect = cv2.add(horiz,vert)

# thin
kernel = np.ones((3,3), np.uint8)
rect = cv2.morphologyEx(rect, cv2.MORPH_ERODE, kernel)

# get largest contour
contours = cv2.findContours(rect, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
for c in contours:
    area_thresh = 0
    area = cv2.contourArea(c)
    if area > area_thresh:
        area = area_thresh
        big_contour = c

# get rotated rectangle from contour
rot_rect = cv2.minAreaRect(big_contour)
box = cv2.boxPoints(rot_rect)
box = np.int0(box)
print(box)

# draw rotated rectangle on copy of img
rot_bbox = img.copy()
cv2.drawContours(rot_bbox,[box],0,(0,0,255),2)

# write img with red rotated bounding box to disk
cv2.imwrite("rectangle_thresh.png", thresh)
cv2.imwrite("rectangle_outline.png", rect)
cv2.imwrite("rectangle_bounds.png", rot_bbox)

# display it
cv2.imshow("IMAGE", img)
cv2.imshow("THRESHOLD", thresh)
cv2.imshow("MORPH", morph)
cv2.imshow("VERT", vert)
cv2.imshow("HORIZ", horiz)
cv2.imshow("RECT", rect)
cv2.imshow("BBOX", rot_bbox)
cv2.waitKey(0)


二值化图像:

在此输入图片描述

矩形区域提取:

在此输入图片描述

图像上的旋转包围框:

在此输入图片描述

旋转包围框角点:

[[446 335]
 [163 328]
 [168 117]
 [451 124]]


补充说明:

这里提供了一个较短的代码版本,可以通过在阈值化之前添加一些高斯模糊来实现。

import cv2
import numpy as np

# read image
img = cv2.imread("rectangle.png")

# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = 255-gray

# blur image
blur = cv2.GaussianBlur(gray, (3,3), 0)

# do adaptive threshold on gray image
thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 75, 2)
thresh = 255-thresh

# apply morphology
kernel = np.ones((5,5), np.uint8)
rect = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
rect = cv2.morphologyEx(rect, cv2.MORPH_CLOSE, kernel)

# thin
kernel = np.ones((5,5), np.uint8)
rect = cv2.morphologyEx(rect, cv2.MORPH_ERODE, kernel)

# get largest contour
contours = cv2.findContours(rect, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
for c in contours:
    area_thresh = 0
    area = cv2.contourArea(c)
    if area > area_thresh:
        area = area_thresh
        big_contour = c

# get rotated rectangle from contour
rot_rect = cv2.minAreaRect(big_contour)
box = cv2.boxPoints(rot_rect)
box = np.int0(box)
for p in box:
    pt = (p[0],p[1])
    print(pt)

# draw rotated rectangle on copy of img
rot_bbox = img.copy()
cv2.drawContours(rot_bbox,[box],0,(0,0,255),2)

# write img with red rotated bounding box to disk
cv2.imwrite("rectangle_thresh.png", thresh)
cv2.imwrite("rectangle_outline.png", rect)
cv2.imwrite("rectangle_bounds.png", rot_bbox)

# display it
cv2.imshow("IMAGE", img)
cv2.imshow("THRESHOLD", thresh)
cv2.imshow("RECT", rect)
cv2.imshow("BBOX", rot_bbox)
cv2.waitKey(0)


二值化图像:

enter image description here

矩形区域提取:

enter image description here

图像上的旋转边界框:

enter image description here

旋转边界框角点:

(444, 335)
(167, 330)
(170, 120)
(448, 125)

1
请看我的回答中的补充,其中包含一个稍微更高效的代码版本。 - fmw42

4
这里有一种简单的方法:
  • 获取二进制图像。 我们加载图像,将其灰度化、高斯模糊,然后进行自适应阈值处理。

  • 形态学操作。 我们创建一个矩形卷积核,并应用形态学开运算以去除噪声

  • 找到畸变矩形轮廓并绘制到蒙版上。 找到轮廓,确定旋转边界框,并绘制到一个空白蒙版上

  • 查找角点。 我们使用已经实现的Shi-Tomasi角点检测器 cv2.goodFeaturesToTrack,这被认为比Harris角点检测器显示更好的结果


以下是每个步骤的可视化:

二进制图像

形态学开运算

找到旋转矩形轮廓并绘制/填充到空白蒙版上

绘制旋转矩形和角点以获得结果

角点坐标

(448.0, 337.0)
(164.0, 332.0)
(452.0, 123.0)
(168.0, 118.0)

代码

import cv2
import numpy as np

# Load image, grayscale, Gaussian blur, adaptive threshold
image = cv2.imread("1.png")
mask = np.zeros(image.shape, dtype=np.uint8)
gray = 255 - cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 51, 3)

# Morph open
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)

# Find distorted rectangle contour and draw onto a mask
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
rect = cv2.minAreaRect(cnts[0])
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(image,[box],0,(36,255,12),2)
cv2.fillPoly(mask, [box], (255,255,255))

# Find corners on the mask
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(mask, maxCorners=4, qualityLevel=0.5, minDistance=150)

for corner in corners:
    x,y = corner.ravel()
    cv2.circle(image,(x,y),8,(255,120,255),-1)
    print("({}, {})".format(x,y))

cv2.imshow("thresh", thresh)
cv2.imshow("opening", opening)
cv2.imshow("mask", mask)
cv2.imshow("image", image)
cv2.waitKey(0)

2
您可以尝试使用自适应 阈值。然后,如果您只需要角点,可以使用 cornerHarris;或者根据接下来要做的事情,您还可以找到有用的 findContours,它返回一个边界框列表。

我之前尝试过自适应阈值,但它返回一个非常嘈杂的图像,使得过滤角落更加困难。我正在尝试仅获取角落以基于另一张图像获得仿射变换矩阵,该图像更容易获取其角落。 - ar018
你可以尝试使用闭包运算符,或者在这里查看 https://docs.opencv.org/3.4/d4/d1b/tutorial_histogram_equalization.html。如果明天我有机会设置一个测试环境,我会深入研究一下。 - Net_Raider

2

我能够找到4个点中的3个,由于是矩形,只需要知道其他三个点就可以轻松找到第四个点。以下是我的解决办法:

import cv2
import numpy as np

img = cv2.imread('6dUIr.png',1)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#smooth the image
kernel = np.ones((5,5),np.float32)/25
gray = cv2.filter2D(gray,-1,kernel)

#histogram equalization
clahe = cv2.createCLAHE(clipLimit=1.45, tileGridSize=(4,4))
cl1 = clahe.apply(gray)

#find edges
edges = cv2.Canny(cl1,4,100)

#find corners
dst = cv2.cornerHarris(edges,2,3,0.04)
#result is dilated for marking the corners, not important
dst = cv2.dilate(dst,None)
# Threshold for an optimal value, it may vary depending on the image.
img[dst>0.25*dst.max()]=[0,0,255]

cv2.imshow('edges', edges)
cv2.imshow('output', img)
# cv2.imshow('Histogram equalized', img_output)

cv2.waitKey(0)

代码中有很多硬编码的阈值,但这是一个不错的开始。

三个点对我来说已经足够了。我只需要图像的视角来对另一张图片进行仿射变换。谢谢你的帮助! - ar018

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