如何从Python中的模糊图像中找到扭曲矩形的准确角位置?

Rob*_*505 5 python opencv image image-processing corner-detection

我正在寻找一个程序,可以使用 Python 中的 OpenCV 准确检测扭曲矩形的角点。

我已经尝试通过谷歌搜索来解决不同的建议,但是通过直线的正弦叠加(参见阈值图像)我可能无法检测到角点。到目前为止,我尝试了 findContours 和 HoughLines,但没有取得好的结果。不幸的是我不明白Xu Bin关于如何使用opencv找到模糊角位置的C代码?

这是我的初始图像:

在此输入图像描述

调整大小和阈值后,我应用精明的边缘检测来获得以下图像:

contours, hierarchy = cv2.findContours(g_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
box = cv2.minAreaRect(contour)
box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
box = np.array(box, dtype="float")
box = perspective.order_points(box)
Run Code Online (Sandbox Code Playgroud)

我仅通过一些额外的绘图得到以下结果:

我认为直线拟合是解决问题的好方法,但不幸的是我无法让 HoughLines 工作,并且在查看OpenCV Python - How to Implement RANSAC to detector Straightlines?之后我无法让 HoughLines 工作。RANSAC似乎也很难适用于我的问题。

非常感谢任何帮助。

Ani*_*wal 6

虽然这已经很旧了,但这至少可以帮助其他有同样问题的人。除了 nathancy 的答案之外,这应该可以让您更准确地找到非常模糊的角落:

伪代码

  1. 如果需要,可以调整大小,但不是必需的
  2. 转换为灰度
  3. 应用模糊或双边过滤
  4. 应用大津阈值得到二值图像
  5. 找到构成矩形的轮廓
  6. 近似矩形的轮廓
  7. 近似点是矩形的角!

执行此操作的代码

  1. 调整大小:
    该函数采用新的宽度和高度,因此我只是将图像比当前大 5 倍。
img = cv2.resize(img, (img.shape[0] * 5, img.shape[1] * 5))
Run Code Online (Sandbox Code Playgroud)

调整大小

  1. 灰度转换:
    只是从 OpenCV 的默认 BGR 色彩空间转换为灰度。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
Run Code Online (Sandbox Code Playgroud)

灰色的

  1. 模糊/双边过滤:
    如果需要,您可以使用任意数量的技术来进一步柔化该图像。也许是高斯模糊,或者,正如 Nathancy 所建议的,双边滤波器,但不需要两者兼而有之。
# choose one, or a different function
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
blurred = cv2.bilateralFilter(gray, 9, 75, 75)
Run Code Online (Sandbox Code Playgroud)

模糊

  1. Otsu 阈值
    使用阈值函数,传递0255作为阈值和最大值的参数。我们通过0是因为我们使用阈值技术cv2.THRESH_OTSU来确定我们的值。它与阈值本身一起返回,但我只是将其设置为_因为我们不需要它。
_, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_OTSU)
Run Code Online (Sandbox Code Playgroud)

脱粒

  1. 查找轮廓 轮廓的
    内容比我在这里解释的要多得多,请随意查看文档。对我们来说,重要的是它返回轮廓列表以及层次结构。我们不需要层次结构,因此将其设置为_,并且我们只需要它找到的单个轮廓,因此我们将其设置为contour = contours[0]
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour = contours[0]
Run Code Online (Sandbox Code Playgroud)

轮廓

  1. 将轮廓近似为矩形
    首先我们计算轮廓的周长。然后我们用该函数对其进行近似cv2.approxPolyDP,并用 告诉它原始曲线与其近似值之间的最大距离0.05 * perimeter。您可能需要使用小数以获得更好的近似值。
    approx是一个具有 shape 的 numpy 数组(num_points, 1, 2),在本例中是(4, 1, 2)因为它找到了矩形的 4 个角。

请随意阅读文档中的更多内容。

perimeter = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.05 * perimeter, True)
Run Code Online (Sandbox Code Playgroud)
  1. 找到你的倾斜矩形!
    你已经完成了!以下是绘制这些点的方法。首先,我们通过循环绘制圆形,然后获取 x 和 y 坐标,然后绘制矩形本身。
# drawing points
for point in approx:
    x, y = point[0]
    cv2.circle(img, (x, y), 3, (0, 255, 0), -1)

# drawing skewed rectangle
cv2.drawContours(img, [approx], -1, (0, 255, 0))
Run Code Online (Sandbox Code Playgroud)

完成的产品

完成代码

import cv2

img = cv2.imread("rect.png")

img = cv2.resize(img, (img.shape[0] * 5, img.shape[1] * 5))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.bilateralFilter(gray, 9, 75, 75)
_, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_OTSU)

contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour = contours[0]

perimeter = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.05 * perimeter, True)

for point in approx:
    x, y = point[0]
    cv2.circle(img, (x, y), 3, (0, 255, 0), -1)
cv2.drawContours(img, [approx], -1, (0, 255, 0))
Run Code Online (Sandbox Code Playgroud)


nat*_*ncy 2

要检测角点,您可以使用cv2.goodFeaturesToTrack(). 该函数有四个参数

corners = cv2.goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance)
Run Code Online (Sandbox Code Playgroud)
  • image- 输入8位或浮点32位灰度单通道图像
  • maxCorners- 返回的最大角数
  • qualityLevel- 0-1 之间的角点的最低可接受质量水平。所有低于质量水平的角都被拒绝
  • minDistance- 角点之间的最小可能欧几里得距离

现在我们知道如何找到角点,我们必须找到旋转的矩形并应用该函数。这是一种方法:


我们首先放大图像,转换为灰度,应用双边滤波器,然后大津阈值得到二值图像

接下来,我们通过查找轮廓来找到扭曲的矩形,cv2.findContours()然后获得以绿色突出显示的旋转边界框。我们将这个边界框绘制到蒙版上

现在我们有了蒙版,我们只需用来cv2.goodFeaturesToTrack()查找蒙版上的角点

这是原始输入图像的结果和(x, y)每个角的坐标

角点

(377.0, 375.0)
(81.0, 344.0)
(400.0, 158.0)
(104.0, 127.0)
Run Code Online (Sandbox Code Playgroud)

代码

import cv2
import numpy as np
import imutils

# Resize image, blur, and Otsu's threshold
image = cv2.imread('1.png')
resize = imutils.resize(image, width=500)
mask = np.zeros(resize.shape, dtype=np.uint8)
gray = cv2.cvtColor(resize, cv2.COLOR_BGR2GRAY)
blur = cv2.bilateralFilter(gray,9,75,75)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Find distorted rectangle contour and draw onto a mask
cnts = cv2.findContours(thresh, 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(resize,[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(resize,(x,y),8,(155,20,255),-1)
    print("({}, {})".format(x,y))

cv2.imshow('resize', resize)
cv2.imshow('thresh', thresh)
cv2.imshow('mask', mask)
cv2.waitKey()
Run Code Online (Sandbox Code Playgroud)