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

时间:2017-06-21 13:22:40

标签: python opencv image-processing pattern-matching

我遇到了使用opencv检测广场的问题。

  1. 这是参考图片,我在其中找到与cv2.matchShapes中使用的形状相匹配的形状
  2. reference image with just square

    1. 以下是两个用于与参考图像进行比较的输入图像:
    2. 第一张图片:

      reference image with same square with a five inside of it

      第二张图片:

      reference image with same square with a five inside of it and assorted symbols and letters

      1. 以下是我使用的代码(抱歉格式不佳):

        #!/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()
        
      2. 分别为两个输入图像的结果:

        第一个结果:

        console out put highlighting zero difference between contours, with highlighted contour area for square for first input image

        第二个结果:

        console out put highlighting 4.21 difference between contours, with highlighted contour area for square for second input image

        ENV:

        python 2.7.10

        opencv 3.2.0

        问题:

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

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

1 个答案:

答案 0 :(得分:4)

查看有关这些函数的文档,看起来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)

无论我用什么数字来修改aboutPolyDP中的第二个参数epsilon,我都得不到4分。我会得到三角形或大量的积分(这个碰巧是六个我相信)。在你的普通代码中,你将arclength乘以0.1,在这种情况下,通常只会给你每个轮廓2个点!

对我来说,这会返回:

enter image description here

看起来右上方的空白区域导致问题,您可能想要模糊,看着您的代码,看起来好像您已经知道这可能是一个问题。重新添加此处标记的注释掉的行:

    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并稍微修改代码以获得更合理的输出):

enter image description here

使用第二张图片尝试模糊技巧也有效!

enter image description here

如果你看一下实际的灰度图像,这是有效的,因为它的模糊程度足以使轮廓足够接近,使大约可以工作并“跳过”之前导致问题的小间隙。这是模糊图像的样子:

enter image description here