python cv2在多个图像中错误地检测正方形

Posted

技术标签:

【中文标题】python cv2在多个图像中错误地检测正方形【英文标题】:python cv2 incorrectly detecting square shape in multiple images 【发布时间】:2017-06-21 13:22:39 【问题描述】:

我在使用 opencv 检测正方形时遇到了问题。

    这是参考图像,我在其中找到了 cv2.matchShapes 中使用的匹配形状

    以下是用于与参考图像进行比较的两个输入图像:

第一张图片:

第二张图片:

    这是我使用的代码(抱歉格式不好):

    #!/usr/bin/env python
    # coding: utf-8
    
    import numpy as np
    import cv2
    import argparse
    import math
    
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--image", required=True, help="path to input image")
    args = vars(ap.parse_args())
    
    img = cv2.imread(args["image"])
    # img = cv2.bitwise_not(img,img)
    # gray = cv2.imread(args["image"],0)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # gray = cv2.GaussianBlur(gray, (5, 5), 0)
    cv2.imshow('gray', gray)
    cv2.waitKey(0)
    
    ret, thresh = cv2.threshold(gray, 230, 255, 1)
    cv2.imshow('thresh', thresh)
    # cv2.imwrite('./wni230.png',thresh)
    cv2.waitKey(0)
    
    square_cnts = []
    
    ##################################################
    shape = cv2.imread('./shape1.png')
    shape_gray = cv2.cvtColor(shape, cv2.COLOR_BGR2GRAY)
    ret, shape_thresh = cv2.threshold(shape_gray, 0, 255, 0)
    tmpimage, contours, h = cv2.findContours(shape_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    shape_cnt0 = contours[0]
    shape_approx = []
    for i in contours:
        approx = cv2.approxPolyDP(i, 0.01*cv2.arcLength(i, True), True)
        # print len(approx)
        if len(approx) == 4:
            shape_approx.append(len(approx))
    ##################################################
    # tmpimage,contours,h = cv2.findContours(thresh,1,2)
    # cv2.RETR_TREE
    tmpimage, contours, h = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnt0 = contours[0]
    # from skimage import measure
    # contours = measure.find_contours(thresh, 0.8)
    
    for cnt in contours[::-1]:
        print cv2.arcLength(cnt, True)
        print cnt
        approx = cv2.approxPolyDP(cnt, 0.1*cv2.arcLength(cnt, True), True)
        print len(approx)
        if len(approx) == 5:
            print "pentagon"
            cv2.drawContours(img, [cnt], 0, 255, 2)
            cv2.imshow('tmppentagon', img)
            cv2.waitKey(0)
        elif len(approx) == 3:
            print "triangle"
            cv2.drawContours(img, [cnt], 0, (0, 255, 0), 2)
            cv2.imshow('tmptriangle', img)
            cv2.waitKey(0)
        elif len(approx) == 2:
            print 'approx:'
            print approx
            ret = cv2.matchShapes(cnt, cnt0, 1, 0.0)
            print 'match shape ret:%s' % ret
            print "two approx line"
            cv2.drawContours(img, [cnt], 0, (0, 255, 0), 2)
            cv2.imshow('twoline?', img)
            cv2.waitKey(0)
        elif len(approx) == 4:
    
            ret = cv2.matchShapes(cnt, cnt0, 1, 0.0)
            print 'match shape ret:%s' % ret
            if ret > 0.5:
                print "Parallelogram"
            elif 0.3 < ret < 0.5:
                print "Rectangle"
            elif 0 < ret < 0.3:
                print "Rhombus"
            else:
                print "square"
            print cv2.arcLength(cnt, True)
            print approx
            cv2.drawContours(img, [approx], 0, (0, 0, 255), 2)
            cv2.imshow('tmpsquare', img)
            cv2.waitKey(0)
            if int(cv2.arcLength(cnt, True)) >= 96:
    
                if math.fabs(math.sqrt((approx[0][0][0]-approx[1][0][0])**2+(approx[0][0][1]-approx[1][0][1])**2) - math.sqrt((approx[1][0][0]-approx[2][0][0])**2+(approx[1][0][1]-approx[2][0][1])**2)) <= 5:
                    x, y, w, h = cv2.boundingRect(cnt)
                    cv2.imshow('final',img[y:y+h,x:x+w])
                    cv2.waitKey(0)
                print 'target but long squere detected...'
                cv2.waitKey(0)
        elif len(approx) == 9:
            print "half-circle"
            cv2.drawContours(img,[cnt],0,(255,255,0),2)
            cv2.imshow('tmphalfcircle',img)
            cv2.waitKey(0)
        elif len(approx) > 15:
            print "circle"
            cv2.drawContours(img,[cnt],0,(0,255,255),2)
            cv2.imshow('tmpcircle',img)
            cv2.waitKey(0)
    
    cv2.imshow('img', img)
    cv2.imwrite('tmp.png', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

两个输入图像的结果:

第一个结果:

第二个结果:

环境:

python 2.7.10

opencv 3.2.0

问题:

    为什么两个图像的 cv2.approxPolyDP 的长度总是 2 而不是 4?我希望图像中正方形的结果为 4。

    为什么我的cv2.matchShapes 结果如此不同?我认为 0 是理想的输出,但是为什么与第二张图像匹配会产生如此高的数字呢?

【问题讨论】:

【参考方案1】:

查看这些函数的文档,approxPolyDP 似乎返回 2,因为它只能找到 2 个点的轮廓,这些点实际上以您描述的方式连接多边形。看看Ramer-Douglas-Peucker 算法,这是 polyDP 的基础。同样,如果两个匹配的形状之间存在很大差异,则 shapeMatch 结果很高。通常,除非形状真的不同,否则该值不会那么大,但在这种情况下,您似乎正在匹配第二张图像的轮廓!

看这里:

tmpimage, contours, h = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt0 = contours[0]

cnt0 是第二张图片中的第一个轮廓。现在你说:

 elif len(approx) == 2:
        print 'approx:'
        print approx
        ret = cv2.matchShapes(cnt, cnt0, 1, 0.0)

您正在将提供的图像中的第一个与同一图像中的轮廓进行比较!第一个的差异为零,因为它恰好是图像中的第一个轮廓,而另一个测试图像中的垃圾更多,这种情况不太可能发生,因此您不会得到匹配。

此外,您似乎永远不会获得四点approxPolyDP,因为您的轮廓没有连接/从正确的位置开始。为了向自己证明这一点,请在循环顶部使用以下代码:

for cnt in contours[::-1]:
    print cv2.arcLength(cnt, True)
    approx_polygon_shape = cv2.approxPolyDP(cnt, 0.087 * cv2.arcLength(cnt, True), True)
    print "number of approx points", len(approx_polygon_shape)
    print "approx shape", approx_polygon_shape
    tempimage = input_image.copy()
    cv2.drawContours(tempimage, [approx_polygon_shape], -1, (0,255,0), 1)
    cv2.imshow("polyshape show", tempimage)
    cv2.waitKey(0)

无论我输入什么数字来修改 approxPolyDP 中的第二个参数 epsilon,我都无法得到四分。我会得到三角形或大量的点(我相信这个恰好是六个)。在您的正常代码中,您将弧长乘以 0.1,在这种情况下,通常每个轮廓只会给您 2 个点!

对我来说,这会返回:

似乎是右上角的空白区域导致了问题,您可能想模糊一下,看看您的代码,似乎您已经知道这可能是一个问题。重新添加此处标记的注释行:

    img = cv2.imread(args["image"])
    # img = cv2.bitwise_not(img,img)
    # gray = cv2.imread(args["image"],0)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    #UNCOMMENT!!!VVV
    gray = cv2.GaussianBlur(gray, (5, 5), 0)
    cv2.imshow('gray', gray)
    cv2.waitKey(0)

即使使用正常的0.1 乘法,也只允许选择四个点的足够模糊(示例输出,请注意我不使用 python2 并稍微修改了代码以获得更合理的输出):

用你的第二张图片尝试模糊技巧也可以!

如果您查看实际的灰度图像,这是可行的,因为它的模糊程度刚好足以让轮廓足够接近 approxPolyDP 工作并“跳过”之前导致问题的那个小间隙。这是模糊图像的样子:

【讨论】:

啊...我在调试另一个问题时评论模糊。无论如何,感谢您的回复,我会接受作为答案

以上是关于python cv2在多个图像中错误地检测正方形的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点#Python图像处理,cv2模块,OpenCV实现人脸检测

在python中从opencv中分离多个canny边缘检测的坐标

#yyds干货盘点#Python图像处理,cv2模块,OpenCV实现模板匹配

Python-OpenCV中的图像轮廓检测

通过 Python 进行人脸检测程序中的 CV2 错误

在加载“cv2”二进制扩展时检测到递归