好吧,这个问题说明了一切
我有一些轮廓,我想从中得到最好的四边形
我遇到了圆角卡片的问题,即使我approxPolyDP()
只能返回 4 个点/边缘,它也永远不是最好的,因为“角落”经常被选为更接近这些圆角边缘。我的解决方案包括增加返回到拐角处的点数,然后我的问题让我可以相对安全地假设找到的四个最长边代表边。按顺序取这四个最长的边,找到每对相交的位置,你就有了四边形的点。
作为奖励,您已经有了长度,因此您可以以正确的顺序将点输入到您的透视变换中,这样它就不会以错误的方式被挤压和拉伸。
您必须使用嵌套区间的方法找到一个合适的值epsilon
,因为点的数量随着 的减少而增加epsion
。但是,可能无法达到 4 个角点的值,因为在某个 epsilon 值下,点的数量可能会从 3 跳到 5。
如果您想排除这种情况,您可能需要自己实现 Ramer-Douglas-Peucker 算法并对其进行修改,因此将返回给定数量的点。(顺便说一句,您要的是“最佳” 4 个角。您必须指定最佳含义。approxPolyDp()
不要求最佳解决方案!)
除此之外,我认为没有办法强制approxPolyDP()
退回 4 分。
我相信您可以对值执行二进制搜索epsilon
以找到最佳简化。
编码:
def simplify_contour(contour, n_corners=4):
'''
Binary searches best `epsilon` value to force contour
approximation contain exactly `n_corners` points.
:param contour: OpenCV2 contour.
:param n_corners: Number of corners (points) the contour must contain.
:returns: Simplified contour in successful case. Otherwise returns initial contour.
'''
n_iter, max_iter = 0, 100
lb, ub = 0., 1.
while True:
n_iter += 1
if n_iter > max_iter:
return contour
k = (lb + ub)/2.
eps = k*cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, eps, True)
if len(approx) > n_corners:
lb = (lb + ub)/2.
elif len(approx) < n_corners:
ub = (lb + ub)/2.
else:
return approx