将四边形 (Tetragon) 拟合到一个 blob

Fit Quadrilateral (Tetragon) to a blob

应用不同的过滤和分割技术后,我得到了这样一张图像:

我可以访问一些轮廓检测功能,这些功能 return 该对象边缘上的点列表,或 return 一个拟合多边形(虽然有很多边,远超过 4 个) . 我想要一种使四边形适合该形状的方法,因为我知道它是应该是四边形的鞋盒的正面。由于透视图的原因,平行度不守恒,所以我暂时没有限制,只需要四个线段包围这个盒子。

到目前为止我能找到的只是矩形拟合,这并不是我真正需要的结果 return 因为它迫使拟合的四边形成为矩形。

如果我可以访问相机与鞋盒的相对角度并且知道鞋盒与相机的距离,我可以生成单应矩阵并扭曲图像,使鞋盒再次呈现矩形,但对于现在我无法访问这些信息,并且想纯粹基于视觉来做。

有解决此类问题的已知方法吗?

我推荐以下步骤:

  1. threshold() 图片
  2. dilate() the image - 这将移除分隔顶部和底部的黑线以及下部较暗的伪影
  3. findContours() 使用设置仅检索外部轮廓(RETR_EXTERNAL) 并简化输出(CHAIN_APPROX_SIMPLE)
  4. 进一步处理轮廓[=​​82=]

步骤1:threshold

# threshold image
ret,thresh = cv2.threshold(img,127,255,0)
cv2.imshow('threshold ',thresh)

步骤2:dilate

# dilate thresholded image - merges top/bottom 
kernel = np.ones((3,3), np.uint8)
dilated = cv2.dilate(thresh, kernel, iterations=3)
cv2.imshow('threshold dilated',dilated)

第 3 步:找到轮廓

# find contours
contours, hierarchy = cv2.findContours(dilated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, 0, (255,255,255), 3)
print "contours:",len(contours)
print "largest contour has ",len(contours[0]),"points"

请注意,首先进行扩张,然后使用简单的外部轮廓得到您想要的形状,但它仍然相当复杂(包含 279 个点)

从现在开始,您可以进一步 process the contour features。 有几个选项可用,例如:

a:获得最小值。区域矩形

# minAreaRect
rect = cv2.minAreaRect(contours[0])
box = cv2.cv.BoxPoints(rect)
box = np.int0(box)
cv2.drawContours(img,[box],0,(255,255,255),3)

可能有用,但不完全是您需要的。

b: 凸包

# convexHull
hull = cv2.convexHull(contours[0])
cv2.drawContours(img, [hull], 0, (255,255,255), 3)
print "convex hull has ",len(hull),"points"

好多了,但你还有 22 点要处理,而且还不够紧张

c: 简化轮廓

# simplify contours

epsilon = 0.1*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
cv2.drawContours(img, [approx], 0, (255,255,255), 3)
print "simplified contour has",len(approx),"points"

这可能就是您想要的:仅 4 分。 如果您需要更多积分,可以使用 epsilon 值。

切记,现在您有了四边形,但图片变平了:没有关于 perspective/3d 旋转的信息。

完整的OpenCV Python代码清单(comment/uncomment根据需要,参考适配c++/java/etc):

import numpy as np
import cv2

img = cv2.imread('XwzWQ.png',0)

# threshold image
ret,thresh = cv2.threshold(img,127,255,0)
cv2.imshow('threshold ',thresh)

# dilate thresholded image - merges top/bottom 
kernel = np.ones((3,3), np.uint8)
dilated = cv2.dilate(thresh, kernel, iterations=3)
cv2.imshow('threshold dilated',dilated)

# find contours
contours, hierarchy = cv2.findContours(dilated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# cv2.drawContours(img, contours, 0, (255,255,255), 3)
print "contours:",len(contours)
print "largest contour has ",len(contours[0]),"points"

# minAreaRect
# rect = cv2.minAreaRect(contours[0])
# box = cv2.cv.BoxPoints(rect)
# box = np.int0(box)
# cv2.drawContours(img,[box],0,(255,255,255),3)

# convexHull
# hull = cv2.convexHull(contours[0])
# cv2.drawContours(img, [hull], 0, (255,255,255), 3)
# print "convex hull has ",len(hull),"points"

# simplify contours
epsilon = 0.1*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
cv2.drawContours(img, [approx], 0, (255,255,255), 3)
print "simplified contour has",len(approx),"points"


# display output 
cv2.imshow('image',img)

cv2.waitKey(0)
cv2.destroyAllWindows()