【问题标题】:Finding the corners of a rectangle寻找矩形的角
【发布时间】:2020-02-04 16:51:59
【问题描述】:

我正在尝试获取这个矩形的角:

.

我尝试使用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()

【问题讨论】:

  • 你能分享一个minimal reproducible example吗?我不确定这是否适合 Stack Overflow。请参阅:How to Askhelp center
  • 我认为描述我所做的就足够了。下次我将分享一个可重现的代码。感谢您的建议。
  • 为什么只有下次?您仍然可以并且应该编辑您的帖子,不是吗?
  • 完成。因为问题已经解决了。

标签: python image opencv image-processing computer-vision


【解决方案1】:

我会通过在自适应阈值处理后获取轮廓的旋转边界框的角来以不同的方式处理它。这是我在 Python/OpenCV 中的代码。

输入:

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)


阈值图像:

矩形区域提取:

图像上的旋转边界框:

旋转边界框角:

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

【讨论】:

  • 在我的答案中查看我的补充,代码的效率更高。
【解决方案2】:

这是一个简单的方法:

  • 获取二值图像。我们加载图像,灰度,高斯模糊,然后自适应阈值。

  • 形态学运算。我们创建一个矩形核并变形开放以去除小噪声

  • 查找扭曲的矩形轮廓并绘制到蒙版上。查找轮廓,确定旋转的边界框,然后绘制到空白蒙版上

  • 查找角点。我们使用已实现为 cv2.goodFeaturesToTrack 的 Shi-Tomasi 角点检测器,与 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)

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 我之前尝试过adaptiveThreshold,但它会返回一个非常嘈杂的图像,更难以过滤角落。我试图只获得角落以获得基于另一个更容易获得其角落的图像的仿射变换矩阵。
    • 您可以尝试使用闭包运算符或查看docs.opencv.org/3.4/d4/d1b/tutorial_histogram_equalization.html。如果明天我有机会设置一个测试环境,我会更深入地了解
    【解决方案4】:

    我能够找到 4 个点中的 3 个,第 4 个点可以很容易地找到其他三个点,因为它是矩形。这是我的解决方案:

    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)

    代码有许多硬编码阈值,但这是一个好的开始。

    【讨论】:

    • 三分对我来说足够了。我只需要图像的透视图就可以对另一个图像进行仿射变换。感谢您的帮助!
    猜你喜欢
    • 2020-06-21
    • 1970-01-01
    • 2014-02-27
    • 2012-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-11
    相关资源
    最近更新 更多