将四边形(四边形)拟合到 blob [英] Fit Quadrilateral (Tetragon) to a blob

查看:15
本文介绍了将四边形(四边形)拟合到 blob的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我可以访问一些轮廓检测函数,这些函数会返回该对象边缘上的点列表,或者返回一个拟合的多边形(虽然有很多边,但远不止 4 个).我想要一种将四边形拟合到该形状的方法,因为我知道它是应该是四边形的鞋盒的正面.由于透视图,平行度不守恒,所以我现在没有限制,只需要包含这个框的四个线段.

到目前为止我能找到的只是矩形拟合,它并没有真正返回我需要的结果,因为它强制拟合的四边形为矩形.

如果我可以访问相机与鞋盒的相对角度,并且知道鞋盒与相机的距离,我可以生成一个单应矩阵并扭曲图像,使鞋盒再次显示为矩形,但现在我无法访问此类信息,并且希望仅基于愿景来进行.

任何已知的解决此类问题的方法?

解决方案

我推荐以下步骤:

  1. threshold()图片
  2. 第 2 步:扩张

    # 扩张阈值图像 - 合并顶部/底部内核 = np.ones((3,3), np.uint8)扩张= cv2.dilate(阈值,内核,迭代= 3)cv2.imshow('阈值扩张',扩张)

    第 3 步:寻找轮廓

    # 查找轮廓轮廓,层次结构 = cv2.findContours(扩张,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img, 轮廓, 0, (255,255,255), 3)打印轮廓:",len(轮廓)print "最大轮廓有 ",len(contours[0]),"points"

    请注意,首先扩张,然后使用简单的外部轮廓可以得到你想要的形状,但它仍然非常复杂(包含 279 个点)

    从现在开始,您可以进一步

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

    b:凸包

    #convexHullhull = cv2.convexHull(contours[0])cv2.drawContours(img, [船体], 0, (255,255,255), 3)print "凸包有 ",len(hull),"points"

    更好,但你还有 22 点要处理,而且还没有可能的紧张

    c:简化轮廓

    # 简化轮廓epsilon = 0.1*cv2.arcLength(contours[0],True)约 = cv2.approxPolyDP(轮廓 [0],epsilon,真)cv2.drawContours(img, [大约], 0, (255,255,255), 3)打印简化轮廓有",len(近似),点"

    这可能就是您所追求的:只需 4 分.如果您需要更多积分,可以使用 epsilon 值.

    记住,现在你有一个四边形,但图片是扁平的:没有关于透视/3d 旋转的信息.

    完整的OpenCV Python代码清单(根据需要注释/取消注释,使用参考适应c++/java/等):

    将 numpy 导入为 np导入简历2img = cv2.imread('XwzWQ.png',0)# 阈值图像ret,thresh = cv2.threshold(img,127,255,0)cv2.imshow('阈值',thresh)# 扩大阈值图像 - 合并顶部/底部内核 = np.ones((3,3), np.uint8)扩张= cv2.dilate(阈值,内核,迭代= 3)cv2.imshow('阈值扩张',扩张)# 寻找轮廓轮廓,层次结构 = cv2.findContours(扩张,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)# cv2.drawContours(img, 轮廓, 0, (255,255,255), 3)打印轮廓:",len(轮廓)print "最大轮廓有 ",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)# 凸包# hull = cv2.convexHull(contours[0])# cv2.drawContours(img, [船体], 0, (255,255,255), 3)# print "凸包有 ",len(hull),"points"# 简化轮廓epsilon = 0.1*cv2.arcLength(contours[0],True)大约 = cv2.approxPolyDP(轮廓 [0],epsilon,真)cv2.drawContours(img, [大约], 0, (255,255,255), 3)打印简化轮廓有",len(近似),点"# 显示输出cv2.imshow('图片',img)cv2.waitKey(0)cv2.destroyAllWindows()

    After applying different filtering and segmentation techniques, I end up with an image like this:

    I have access to some contours detection functions that return a list of points on the edge of that object, or returns a fitted polygon (with many edges though, much more than 4). I want a way to fit a quadrilateral to that shape as I know it is a front face of a shoebox that is supposed to be a quadrilateral. Due to the perspective view, the parallelity is not conserved so I have no constraints for now and just need four line segments encompassing this box.

    What I could find until now was only rectangle fitting, which doesn't really returns the result I need as it forces the fitted quadrilateral to be rectangular.

    If I had access to the relative angle of the camera to that shoebox and knew the distance of the shoebox from my camera, I could generate a Homography matrix and warp the image such that the shoebox appears rectangular again, but for now I have no access to such information and want to do it based purely vision.

    Any known approaches for solving such a problem?

    解决方案

    I recommend the following steps:

    1. threshold() the image
    2. dilate() the image - this will remove the black line splitting the top and bottom section and also darker artifacts on the lower part
    3. findContours() using setting to retrieve only external contours(RETR_EXTERNAL) and simplify the output(CHAIN_APPROX_SIMPLE)
    4. process the contours further

    Step 1:threshold

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

    Step 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)
    

    Step 3: find contours

    # 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"
    

    Notice that dilating first, then using simple external contours gets you shape you're after, but it's still pretty complex (containg 279 points)

    From this point onward you can futher process the contour features. There are a few options, available such as:

    a: getting the min. area rectangle

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

    Can be useful, but not exactly what you need.

    b: convex hull

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

    Better, but you still have 22 points to deal with and it's not tight as it could be

    c: simplify contours

    # 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"
    

    This is probably what you're after: just 4 points. You can play with the epsilon value if you need more points.

    Bare in mind, now you have a quad, but the picture is flattened: there's no information on perspective/3d rotation.

    Full OpenCV Python code listing (comment/uncomment as needed, use the reference to adapt to 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()
    

    这篇关于将四边形(四边形)拟合到 blob的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆