zoukankan      html  css  js  c++  java
  • OpenCV-Python 图像阈值 | 十五

    目标

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

    简单阈值

    在这里,问题直截了当。对于每个像素,应用相同的阈值。如果像素值小于阈值,则将其设置为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 numpy as np
    from matplotlib import pyplot as plt
    img = cv.imread('gradient.png',0)
    ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
    ret,thresh2 = cv.threshold(img,127,255,cv.THRESH_BINARY_INV)
    ret,thresh3 = cv.threshold(img,127,255,cv.THRESH_TRUNC)
    ret,thresh4 = cv.threshold(img,127,255,cv.THRESH_TOZERO)
    ret,thresh5 = cv.threshold(img,127,255,cv.THRESH_TOZERO_INV)
    titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
    images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
    for i in xrange(6):
        plt.subplot(2,3,i 1),plt.imshow(images[i],'gray')
        plt.title(titles[i])
        plt.xticks([]),plt.yticks([])
    plt.show()
    

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

    该代码产生以下结果:

    自适应阈值

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

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

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

    cv.ADAPTIVE_THRESH_MEAN_C::阈值是邻近区域的平均值减去常数C
    cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域值的高斯加权总和减去常数C

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

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

    结果:

    Otsu的二值化

    在全局阈值化中,我们使用任意选择的值作为阈值。相反,Otsu的方法避免了必须选择一个值并自动确定它的情况。

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

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

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

    import cv2 as cv
    import numpy as np
    from matplotlib import pyplot as plt
    img = cv.imread('noisy2.png',0)
    # 全局阈值
    ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
    # Otsu阈值
    ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY cv.THRESH_OTSU)
    # 高斯滤波后再采用Otsu阈值
    blur = cv.GaussianBlur(img,(5,5),0)
    ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY cv.THRESH_OTSU)
    # 绘制所有图像及其直方图
    images = [img, 0, th1,
              img, 0, th2,
              blur, 0, th3]
    titles = ['Original Noisy Image','Histogram','Global Thresholding (v=127)',
              'Original Noisy Image','Histogram',"Otsu's Thresholding",
              'Gaussian filtered Image','Histogram',"Otsu's Thresholding"]
    for i in xrange(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()
    

    结果:

    Otsu的二值化如何实现?

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

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

    σw2(t)=q1(t)σ12(t)q2(t)σ22(t) sigma_w^2(t) = q_1(t)sigma_1^2(t) q_2(t)sigma_2^2(t)

    其中

    q1(t)=i=1tP(i)&q2(t)=i=t1IP(i) q_1(t) = sum_{i=1}^{t} P(i) quad & quad q_2(t) = sum_{i=t 1}^{I} P(i)

    μ1(t)=i=1tiP(i)q1(t)&μ2(t)=i=t1IiP(i)q2(t) mu_1(t) = sum_{i=1}^{t} frac{iP(i)}{q_1(t)} quad & quad mu_2(t) = sum_{i=t 1}^{I} frac{iP(i)}{q_2(t)}

    σ12(t)=i=1t[iμ1(t)]2P(i)q1(t)&σ22(t)=i=t1I[iμ2(t)]2P(i)q2(t) sigma_1^2(t) = sum_{i=1}^{t} [i-mu_1(t)]^2 frac{P(i)}{q_1(t)} quad & quad sigma_2^2(t) = sum_{i=t 1}^{I} [i-mu_2(t)]^2 frac{P(i)}{q_2(t)}

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

    img = cv.imread('noisy2.png',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)
    fn_min = np.inf
    thresh = -1
    for i in xrange(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 = i
    # 使用OpenCV函数找到otsu的阈值
    ret, otsu = cv.threshold(blur,0,255,cv.THRESH_BINARY cv.THRESH_OTSU)
    print( "{} {}".format(thresh,ret) )
    

    其他资源

    1. Digital Image Processing, Rafael C. Gonzalez

    练习题

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

    欢迎关注磐创博客资源汇总站:
    http://docs.panchuang.net/

    欢迎关注PyTorch官方中文教程站:
    http://pytorch.panchuang.net/

    OpenCV中文官方文档:
    http://woshicver.com/

  • 相关阅读:
    freemaker获取字符串长度
    freemarker截取字符串subString
    [转]freemarker中的list
    python常用模块——os模块
    python正则表达式
    需要区分对比的函数以及函数小结
    信道极限容量
    信道和调制
    python中颜色设置
    python中的exec()、eval()以及complie()
  • 原文地址:https://www.cnblogs.com/panchuangai/p/12567916.html
Copyright © 2011-2022 走看看