zoukankan      html  css  js  c++  java
  • OpenCVPython系列之轮廓的高级功能

    截至到本次教程,我们已经基本掌握了OpenCV常用的一些功能,实际上已经可以处理很多问题了,故从本教程开始,示例代码将编写为一个固定函数,以便调用,另外将不再给出完整代码,比如导入库将不再另行贴出,一些基本的代码也不再贴出,只贴出核心部分,我会将核心部分整理为一个方便调用的函数。

    我们在前面讨论了轮廓的特征以及属性,今天我们将综合之前学的内容讨论轮廓的高级功能。

    凸缺陷

    对象上的任何凹陷都被称为凸缺陷,OpenCV 中有一个函数cv.convexityDefect()可以帮助我们找到凸缺陷,函数调用如下:

    hull = cv2.convexHull(cnt,returnPoints = False)

    defects = cv2.convexityDefects(cnt,hull)

    注意:如果要查找凸缺陷,在使用函数 cv2.convexHull 找凸包时,
    参数 returnPoints 一定要是 False。

    它会返回一个数组,其中每一行包含的值是 [起点,终点,最远的点,到最远点的近似距离],们可以在一张图上显示它。我们将起点和终点用一条绿线 连接,在最远点画一个圆圈,要记住的是返回结果的前三个值是轮廓点的索引,我们仍然使用之前的多边形的图片:

    image.png

    来看代码核心部分:

    def convexity(img):
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        ret, thresh = cv2.threshold(img_gray, 127, 255, 0)
        contours, hierarchy = cv2.findContours(thresh, 2, 1)
        cnt = contours[0]
        hull = cv2.convexHull(cnt, returnPoints=False)
        defects = cv2.convexityDefects(cnt, hull)
        for i in range(defects.shape[0]):
            s, e, f, d = defects[i, 0]
            start = tuple(cnt[s][0])
            end = tuple(cnt[e][0])
            far = tuple(cnt[f][0])
            cv2.line(img, start, end, [0, 255, 0], 2)
            cv2.circle(img, far, 5, [0, 0, 255], -1)
            cv2.imshow('img', img)

    image.png

    可以看到,凹陷处被检测出来。

    点多边形测试

    点多边形测试求解图像中的一个点到一个对象轮廓的最短距离。如果点在轮廓的外部,返回值为负。如果在轮廓上,返回值为0。如果在轮廓内部,返回值为正。

    例如,我们可以如下检查点(50,50):

    dist = cv.pointPolygonTest(cnt,(50,50),True

    在函数中,第三个参数是measureDist。如果为True,则找到带符号的距离。如果为False,它将查找该点是在轮廓内部还是外部或轮廓上(分别返回+ 1,-1、0)。

    这个按照我们的需要设置为False即可,当measureDist设置为false时,若返回值为+1,表示点在轮廓内部,返回值为-1,表示在轮廓外部,返回值为0,表示在轮廓上。

    注意:如果不想查找距离,确保第三个参数为False,因为这是一个耗时的过程。因此,将其设置为False可使速度提高2-3倍。

    我们来看代码,看看(50,50)位于哪儿:

    def PointPolygon(img):
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        ret, thresh = cv2.threshold(img_gray, 127, 255, 0)
        contours, hierarchy = cv2.findContours(thresh, 2, 1)
        cnt = contours[0]
        dist = cv2.pointPolygonTest(cnt,(50,50),False)
        print(dist)

    结果:

    image.png

    -1代表该点位于轮廓内部。

    匹配形状

    函数 cv2.matchShape() 可以帮我们比较两个形状或轮廓的相似度。如果返回值越小,匹配越好。它是根据 Hu 矩来计算的。Hu 矩是归一化中心矩的线性组合,之所以这样做是为了能够获取代表图像的某个特征的矩函数,这些矩函数对某些变化如缩放,旋转,镜像映射具有不变形。

    我们来看函数原型以及相应参数解释:

    image.png

    第一个参数是待匹配的物体1,第二个是待匹配的物体2

    第三个参数method有三种输入:

    CV_CONTOURS_MATCH_I1

    CV_CONTOURS_MATCH_I2

    CV_CONTOURS_MATCH_I3

    即三种不同的判定物体相似的方法

    image.png

    注意:

    前两个参数输入“灰度图像”时,并不是想当然的那样,其内容包含待匹配轮廓图案的灰度图;而是使用一行或一列双通道灰度图或者两列灰度图,该图中的每个像素不是什么图片,而是代表多边形轮廓上各节点的X,Y坐标。

    输入轮廓时每个参数只能是一个轮廓

    MatchShapes是OpenCV提供的一个根据计算比较两张图像Hu不变距的函数,函数返回值代表相似度大小,完全相同的图像返回值是0,返回值最大是1。这可以用在在一堆照片中搜索出两张相同或相同程度最大的图像。

    我们现在可以做个试验,类似于模板匹配。

    待识别图像:

    image.png

    模板图像:

    image.png

    现在我们看看识别的步骤:

    1. 将待识别图像 -> 灰度图像 -> 二值图像

    2. 通过轮廓检索函数 cv.findContours 找到待识别图像所有轮廓

    3. 模板图像 -> 灰度图像 -> 二值图像

    4. 通过轮廓检索函数 cv.findContours 找到模板图像中字母 A 的外轮廓

    5. 将第2步得到的轮廓逐一和第4步得到的轮廓 通过 cv.matchShapes 函数进行形状匹配。找到其中最小值,最小值对应的待识别图像中的轮廓即为匹配到的模板图像

    6. 标出在待识别图像中找到的模板图像

    来看核心代码:

    def MatchShapes():
        # 载入原图
        img = cv2.imread('OCR.jpg', 0)
        # 在下面这张图像上作画
        image1 = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
    
        # 二值化图像
        _, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY|
                                 cv2.THRESH_OTSU)
        # 搜索轮廓
        contours, hierarchy = cv2.findContours(thresh, 3, 2)
        hierarchy = np.squeeze(hierarchy)
    
        # 载入标准模板图
        img_a = cv2.imread('temp.jpg', 0)
        _, th = cv2.threshold(img_a, 0, 255, cv2.THRESH_BINARY|
                              cv2.THRESH_OTSU)
        contours1, hierarchy1 = cv2.findContours(th, 3, 2)
        # 字母A的轮廓
        template_a = contours1[0]
    
        # 记录最匹配的值的大小和位置
        min_pos = -1
        min_value = 2
        for i in range(len(contours)):
            # 参数3:匹配方法;参数4:opencv预留参数
            value = cv2.matchShapes(template_a, contours[i], 1, 0.0)
            if value < min_value:
                min_value = value
                min_pos = i
    
        # 参数3为0表示绘制本条轮廓contours[min_pos]
        cv2.drawContours(image1, [contours[min_pos]], 0, [255, 0, 0], 3)
    
        cv2.imshow('result', image1)

    image.png

    可以看到已经正确的匹配到了,实际上这就相当于是OCR文字识别的雏形。

    当然,关于OpenCV中轮廓的讨论还没有结束,仍然有其他的功能,我们将在下次介绍。

  • 相关阅读:
    随机算法之舍伍德算法
    KMP算法笔记
    分治法求解最大子段和问题
    递归与分治之间的关系
    微信公众号开发学习笔记(一)
    关于echo$PATH中的PTAH来源
    【Leetcode】807. Max Increase to Keep City Skyline
    【Leetcode】804. Unique Morse Code Words
    【Leetcode】709. To Lower Case
    【Leetcode】Jewels and Stones
  • 原文地址:https://www.cnblogs.com/wuyuan2011woaini/p/15656578.html
Copyright © 2011-2022 走看看