OpenCV竟然可以这样学!成神之路终将不远(十一)

简介: OpenCV竟然可以这样学!成神之路终将不远(十一)

 返回目录

目录

11 图像阈值

11.1 目标

11.2 简单阈值

11.3 自适应阈值

11.4 Otsu的二值化

11.5 Otsu的二值化如何实现?

11.6 练习题


11 图像阈值

11.1 目标

    • 在本教程中,您将学习简单阈值,自适应阈值和Otsu阈值。
    • 你将学习函数cv.thresholdcv.adaptiveThreshold

    11.2 简单阈值

    在这里,问题直截了当。对于每个像素,应用相同的阈值。如果像素值小于阈值,则将其设置为0,否则将其设置为最大值。函数cv.threshold用于应用阈值。

    第一个参数是源图像,它应该是灰度图像。

    第二个参数是阈值,用于对像素值进行分类。

    第三个参数是分配给超过阈值的像素值的最大值。

    OpenCV提供了不同类型的阈值,这由函数的第四个参数给出。通过使用cv.THRESH_BINARY类型。所有简单的阈值类型为:

      • cv.THRESH_BINARY
      • cv.THRESH_BINARY_INV
      • cv.THRESH_TRUNC
      • cv.THRESH_TOZERO
      • cv.THRESH_TOZERO_INV

      请通过类型的文档来观察区别。

      该方法返回两个输出。第一个是使用的阈值,第二个输出是阈值后的图像。

      此代码比较了不同的简单阈值类型:

      import cv2 as cv
      import matplotlib.pyplot as plt
      img = plt.imread('../girl6/05.jpg')
      gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
      _, threshold1 = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
      _, threshold2 = cv.threshold(gray, 127, 255, cv.THRESH_BINARY_INV)
      _, threshold3 = cv.threshold(gray, 127, 255, cv.THRESH_TRUNC)
      _, threshold4 = cv.threshold(gray, 127, 255, cv.THRESH_TOZERO)
      _, threshold5 = cv.threshold(gray, 127, 255, cv.THRESH_TOZERO_INV)
      images = [img, threshold1, threshold2, threshold3, threshold4, threshold5]
      titles = ['image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
      for i in range(6):    # 我这里是python3,取消了xrange,直接改成了range,python2的可以用xrange
          plt.subplot(2, 3, i + 1)
          plt.imshow(images[i])
          plt.title(titles[i])
          plt.xticks([])
          plt.yticks([])
      plt.show()

      image.gif

      注意:为了绘制多个图像,我们使用plt.subplot() 函数。请查看matplotlib文档以获取更多详细信息。

      运行结果如下:

      image.gif编辑

      11.3 自适应阈值

      在上一节中,我们使用一个全局值作为阈值。但这可能并非在所有情况下都很好,例如,如果图像在不同区域具有不同的光照条件。在这种情况下,自适应阈值阈值化可以提供帮助。在此,算法基于像素周围的小区域确定像素的阈值。因此,对于同一图像的不同区域,我们获得了不同的阈值,这为光照度变化的图像提供了更好的结果。

      除上述参数外,方法cv.adaptiveThreshold还包含三个输入参数:

      adaptiveMethod决定阈值是如何计算的:

      cv.ADAPTIVE_THRESH_MEAN_C::阈值是邻近区域的平均值减去常数C。

      cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值的高斯加权总和减去常数C。

      BLOCKSIZE确定附近区域的大小,C是从邻域像素的平均或加权总和中减去的一个常数。

      具体函数参数如下:

        • 第一个原始图像。
        • 第二个像素值上限。
        • 第三个自适应方法adaptiveMethod,具体如上述。
        • 第四个值的赋值方法:只有cv2.THRESH_BINARY 和cv2.THRESH_BINARY_INV
        • 第五个Block size:规定领域大小(一个正方形的领域)
        • 第六个常数C,阈值等于均值或者加权值减去这个常数(为0相当于阈值 就是求得领域内均值或者加权值)

        这种方法理论上得到的效果更好,相当于在动态自适应的调整属于自己像素点的阈值,而不是整幅图像都用一个阈值。

        下面的代码比较了光照变化的图像的全局阈值和自适应阈值:

        import cv2 as cv
        import matplotlib.pyplot as plt
        img = plt.imread('../girl6/05.jpg')
        gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        _, threshold1 = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)  # 这里随便选择一个全局阈值
        threshold2 = cv.adaptiveThreshold(gray, 255,
                                          cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 9, 3)
        threshold3 = cv.adaptiveThreshold(gray, 255,
                                          cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 9, 3)
        images = [img, threshold1, threshold2, threshold3]
        titles = ['Original Image', 'Global Threshold',
                  'Adaptive Mean Threshold', 'Adaptive Gaussian Threshold']
        for i in range(4):
            plt.subplot(2, 2, i + 1)
            plt.imshow(images[i], 'gray')
            plt.title(titles[i])
        plt.show()

        image.gif

        运行结果如下:

        image.gif编辑

        11.4 Otsu的二值化

        在全局阈值化中,我们使用任意选择的值作为阈值。相反,Otsu的方法避免了必须选择一个值并自动确定它的情况。这就很人性化了!好像越来越厉害了。。。

        考虑仅具有两个不同图像值的图像(双峰图像),其中直方图将仅包含两个峰。一个好的阈值应该在这两个值的中间。类似地,Otsu的方法从图像直方图中确定最佳全局阈值。

        为此,使用了cv.threshold作为附加标志传递。阈值可以任意选择。然后,算法找到最佳阈值,该阈值作为第一输出返回。

        查看以下示例。输入图像为噪点图像。在第一种情况下,采用值为127的全局阈值。在第二种情况下,直接采用Otsu阈值法。在第三种情况下,首先使用5x5高斯核对图像进行滤波以去除噪声,然后应用Otsu阈值处理。了解噪声滤波如何改善结果。

        代码如下:

        import cv2 as cv
        import matplotlib.pyplot as plt
        """
        在第一种情况下,采用值为127的全局阈值。
        在第二种情况下,直接采用Otsu阈值法。
        在第三种情况下,首先使用5x5高斯核对图像进行滤波以去除噪声,
        然后应用Otsu阈值处理。了解噪声滤波如何改善结果。
        """
        img = cv.imread('002.jpg', 0)
        # 第一种情况:采用值为127的全局阈值
        _, threshold1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
        # 第二种情况:直接采用Otsu阈值法
        _, threshold2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
        # 第三种情况:首先使用5x5高斯核对图像进行滤波以去除噪声,然后应用Otsu阈值处理
        blur = cv.GaussianBlur(img, (5, 5), 0)
        _, threshold3 = cv.threshold(blur, 127, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
        # 绘制所有图像及直方图
        images = [img, 0, threshold1,
                  img, 0, threshold2,
                  blur, 0, threshold3]
        titles = ['Original Image', 'Histogram', 'Global Threshold',
                  'Original Image', 'Histogram', 'Ostu Threshold',
                  'Gaussian Blur Image', 'Histogram', 'Ostu Threshold']
        for i in range(3):
            plt.subplot(3, 3, i * 3 + 1)
            plt.imshow(images[i * 3], 'gray')
            plt.title(titles[i * 3])
            plt.xticks([])
            plt.yticks([])
            plt.subplot(3, 3, i * 3 + 2)
            plt.hist(images[i * 3].ravel(), 256)
            plt.title(titles[i * 3 + 1])
            plt.xticks([])
            plt.yticks([])
            plt.subplot(3, 3, i * 3 + 3)
            plt.imshow(images[i * 3 + 2], 'gray')
            plt.title(titles[i * 3 + 2])
            plt.xticks([])
            plt.yticks([])
        plt.show()

        image.gif

        运行结果如下:

        image.gif编辑

        11.5 Otsu的二值化如何实现?

        本节演示了Otsu二值化的Python实现,以展示其实际工作方式。如果您不感兴趣,可以跳过此步骤。

        由于我们正在处理双峰图像,因此Otsu的算法尝试找到一个阈值(t),该阈值将由关系式给出的加权类内方差最小化:

        其中:

        实际上,它找到位于两个峰值之间的t值,以使两个类别的差异最小。它可以简单地在Python中实现,如下所示:

        import cv2 as cv
        import numpy as np
        img = cv.imread('001.jpg', 0)
        # 将原图进行高斯处理
        blur = cv.GaussianBlur(img, (5, 5), 0)
        # 寻找归一化直方图
        hist = cv.calcHist([blur], [0], None, [256], [0, 256])
        # 将多维数组展平成一维数组,除以最大值进行归一化处理
        hist_norm = hist.ravel() / hist.max()
        Q = hist_norm.cumsum()  # 按列累加
        bins = np.arange(256)  # 创建一个长度为256的数组
        fn_min = np.inf  # 设定一个无穷大的数作为界限
        thresh = -1
        for i in range(1, 256):
            p1, p2 = np.hsplit(hist_norm, [i])  # 概率
            q1, q2 = Q[i], Q[255] - Q[i]  # 对类求和
            b1, b2 = np.hsplit(bins, [i])  # 权重
            # 寻找均值和方差
            m1, m2 = np.sum(p1 * b1) / q1, np.sum(p2 * b2) / q2
            v1, v2 = np.sum(((b1 - m1) ** 2) * p1) / q1, np.sum(((b2 - m2) ** 2) * p2) / q2
            # 计算最小化函数
            fn = v1 * q1 + v2 * q2
            if fn < fn_min:
                fn_min = fn
                thresh = 1
        # 使用OpenCV函数找到Otsu的阈值
        ret, otsu = cv.threshold(blur, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
        print('{} {}'.format(thresh, ret))

        image.gif

        cv2.calcHist函数具体参数如下:

        cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]]) ->hist iamge

          • imaes:输入的图像
          • channels:选择图像的通道
          • mask:掩膜,是一个大小和image一样的np数组,其中把需要处理的部分指定为1,不需要处理的部分指定为0,一般设置为None,表示处理整幅图像
          • histSize:使用多少个bin(柱子),一般为256
          • ranges:像素值的范围,一般为[0,255]表示0~255
          • 后面两个参数可以不用管

          注意:除了mask,其他四个参数都要带中括[]号。

          11.6 练习题

          Otsu的二值化有一些优化。您可以搜索并实现它。


          欢迎评论区留言,一起探讨OpenCV成神之路的奥秘。

          顺便给我加个关注,点个赞,加个收藏,让我们一起登上神坛。

          image.gif编辑

          相关文章
          |
          机器学习/深度学习 编解码 计算机视觉
          OpenCV竟然可以这样学!成神之路终将不远(十六)
          OpenCV竟然可以这样学!成神之路终将不远(十六)
          |
          计算机视觉 Python
          OpenCV竟然可以这样学!成神之路终将不远(十)
          OpenCV竟然可以这样学!成神之路终将不远(十)
          |
          算法 计算机视觉 Python
          OpenCV竟然可以这样学!成神之路终将不远(二十六)
          OpenCV竟然可以这样学!成神之路终将不远(二十六)
          |
          算法 计算机视觉 索引
          OpenCV竟然可以这样学!成神之路终将不远(二十五)
          OpenCV竟然可以这样学!成神之路终将不远(二十五)
          |
          计算机视觉 Python
          OpenCV竟然可以这样学!成神之路终将不远(二十四)
          OpenCV竟然可以这样学!成神之路终将不远(二十四)
          |
          计算机视觉 Python
          OpenCV竟然可以这样学!成神之路终将不远(二十三)
          OpenCV竟然可以这样学!成神之路终将不远(二十三)
          |
          计算机视觉 索引 Python
          OpenCV竟然可以这样学!成神之路终将不远(二十二)
          OpenCV竟然可以这样学!成神之路终将不远(二十二)
          |
          安全 计算机视觉 索引
          OpenCV竟然可以这样学!成神之路终将不远(二十一)
          OpenCV竟然可以这样学!成神之路终将不远(二十一)
          |
          文字识别 计算机视觉 索引
          OpenCV竟然可以这样学!成神之路终将不远(二十)
          OpenCV竟然可以这样学!成神之路终将不远(二十)
          |
          定位技术 计算机视觉 Python
          OpenCV竟然可以这样学!成神之路终将不远(十九)
          OpenCV竟然可以这样学!成神之路终将不远(十九)
          下一篇
          DDNS