• 【OpenCV实现图像阈值处理】


    概要

    OpenCV库中的图像处理技术,主要分为几何变换、图像阈值调整和平滑处理三个部分。

    几何变换方面,OpenCV提供了cv.warpAffine和cv.warpPerspective函数,用于实现仿射变换和透视变换。这些技术包括缩放、平移、旋转等,通过变换矩阵的运用,可以实现图像的各种变换操作。

    图像阈值调整是将图像的像素值按照一定规则进行二值化处理,OpenCV提供了cv.threshold函数,支持多种阈值处理类型,如二值化、反二值化、截断、阈值以下置零等。还有自适应阈值调整,它可以根据图像的局部特性动态调整阈值,适用于光照不均匀的情况。

    另外,介绍了大津(Otsu’s)阈值法,该方法是一种自动确定全局阈值的技术。通过计算图像的直方图,找到最佳阈值,实现图像的二值化处理。

    简单阈值调整

    简单的阈值调整,自适应阈值和大津阈值法。
    函数 cv.threshold 和 cv.adaptiveThreshold

    在图像处理中,阈值调整是一种基本技术。其核心思想是对图像的每个像素应用相同的阈值规则,如果像素值低于阈值,就将其设置为0,反之则设置为最大值。OpenCV提供了用于阈值调整的函数cv.threshold。该函数的第一个参数是源图像,必须是灰度图像;第二个参数是阈值,用于区分像素值;第三个参数是最大值,当像素值超过阈值时,会被设置为这个值。OpenCV还提供了不同的阈值调整类型,通过第四个参数来选择。常用的阈值调整类型包括:

    cv.THRESH_BINARY:超过阈值的像素值设为最大值,其他设为0。
    cv.THRESH_BINARY_INV:超过阈值的像素值设为0,其他设为最大值。
    cv.THRESH_TRUNC:超过阈值的像素值设为阈值,其他像素值不变。
    cv.THRESH_TOZERO:超过阈值的像素值不变,其他设为0。
    cv.THRESH_TOZERO_INV:超过阈值的像素值设为0,其他不变。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这些类型的具体应用可以根据需求选择。cv.threshold函数返回两个输出:第一个是使用的阈值,第二个是阈值化后的图像。选择合适的阈值调整类型和阈值参数可以有效地处理图像,提取出感兴趣的信息。详细了解每种类型的使用场景,可以参考OpenCV的相关文档以获取更多信息。

    import cv2 as cv
    import numpy as np
    from matplotlib import pyplot as plt
    
    # 读取灰度图像
    img = cv.imread('img.png', 0)
    
    # 使用cv.threshold进行阈值调整,得到不同类型的阈值化图像
    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循环显示图像和标题
    for i in range(6):
        plt.subplot(2, 3, i + 1)  # 2行3列的子图中的第i+1个
        plt.imshow(images[i], 'gray', vmin=0, vmax=255)  # 显示灰度图像,灰度范围0-255
        plt.title(titles[i])  # 设置子图标题
        plt.xticks([]), plt.yticks([])  # 隐藏坐标轴
    
    # 显示图像
    plt.show()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    我们使用 plt.subplot() 函数来排列图像。
    这段代码输出这个结果。
    在这里插入图片描述

    自适应阈值调整

    在图像处理中,使用全局阈值可能不适用于所有情况。例如,当图像在不同区域具有不同的光照条件时,使用固定阈值可能导致不准确的结果。为了解决这个问题,我们可以采用自适应阈值调整的方法。这种方法根据像素周围的小区域确定每个像素的阈值,因此,同一张图像的不同区域可以获得不同的阈值,从而更好地适应图像中的光照变化。

    在OpenCV中,cv.adaptiveThreshold函数用于实现自适应阈值调整。该函数接受三个主要参数:

    adaptiveMethod: 它决定了阈值如何被计算。
        cv.ADAPTIVE_THRESH_MEAN_C:阈值是邻域像素的平均值减去常数C。
        cv.ADAPTIVE_THRESH_GAUSSIAN_C:阈值是邻域像素的高斯加权平均值减去常数C。
    
    blockSize: 它指定了邻域的大小。该值定义了算法在图像中寻找阈值时所考虑的像素邻域范围。
    
    C: 它是一个常数,用于从计算得到的阈值中减去,可以调整阈值的灵敏度。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    通过调整这些参数,我们可以更精确地适应不同区域的光照变化,从而得到更准确的图像处理结果。

    
    import cv2 as cv
    import numpy as np
    from matplotlib import pyplot as plt
    
    img = cv.imread('img.png', 0)
    img = cv.medianBlur(img, 5)
    
    ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
    th2 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C, \
                              cv.THRESH_BINARY, 11, 2)
    th3 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, \
                              cv.THRESH_BINARY, 11, 2)
    
    titles = ['Original Image', 'Global Thresholding (v = 127)',
              'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
    
    images = [img, th1, th2, th3]
    
    for i in range(4):
    
        plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
    
        plt.title(titles[i])
    
        plt.xticks([]),plt.yticks([])
    
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在这里插入图片描述

    大津(Otsu’s)阈值法

    在全局阈值调整中,我们必须手动选择一个适当的阈值来将图像分为目标和背景。然而,在某些情况下,我们无法确定最佳的阈值,尤其是当图像具有复杂的光照变化或噪声时。在这种情况下,使用大津(Otsu)阈值法是一个更好的选择。

    大津阈值法适用于具有双峰直方图(即具有两个明显峰值)的图像。在这种情况下,图像的直方图将具有两个主要的峰值,分别代表目标和背景像素的灰度级别。大津方法的目标是找到一个最佳的阈值,将这两个峰值之间的灰度级别作为分割点。

    在OpenCV中,我们使用cv.threshold()函数,并将cv.THRESH_OTSU作为阈值类型的额外参数传递。大津算法会自动确定最佳阈值,并将其作为函数的返回值。这样,我们就不需要手动选择阈值,而是依赖算法找到最适合的分割点,使图像的分割更准确。

    
    import cv2 as cv
    
    import numpy as np
    
    from matplotlib import pyplot as plt
    
    img = cv.imread('img.png',0)
    
    # 全局阈值
    
    ret1,th1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
    
    # Otsu's
    
    ret2,th2 = cv.threshold(img,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)
    
    # 高斯后Otsu's
    
    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 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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    在这里插入图片描述

    Otsu’s 二值化是如何工作的

    Otsu’s二值化算法是一种自动确定图像阈值的方法。该算法的目标是找到一个阈值 tt,使得将图像的灰度级别分为两个类别(背景和前景),并且这两个类别的内部方差最小。

    在具体实现中,我们首先计算图像的直方图,该直方图显示了不同灰度级别的像素数量。接着,算法尝试在直方图中找到两个主要的峰值,这两个峰值分别代表了背景和前景像素的灰度级别。然后,Otsu’s算法尝试选择一个阈值 tt,将这两个峰值之间的灰度级别作为分割点。

    为了选择最佳的阈值 tt,算法将尝试所有可能的阈值,并计算对应的两个类别的内部方差。内部方差越小,说明两个类别之间的差异越小,图像分割得越好。最终,选择使内部方差最小的阈值 tt 作为最佳阈值,将图像二值化。
    在这里插入图片描述

    这种方法的核心思想是通过寻找使得两个类别差异最小的阈值,实现图像的自动分割。对于了解过线性规划或神经网络的人来说,这个思想可能更容易理解。

    import cv2 as cv
    import numpy as np
    img = cv.imread('img.png',0)
    blur = cv.GaussianBlur(img,(5,5),0)
    
    # 求归一化直方图及其累积分布函数
    
    hist = cv.calcHist([blur],[0],None,[256],[0,256])
    hist_norm = hist.ravel()/hist.sum()
    Q = hist_norm.cumsum()
    
    bins = np.arange(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] # 类型总和
    
        if q1 < 1.e-6 or q2 < 1.e-6:
    
            continue
    
        b1,b2 = np.hsplit(bins,[i]) # weights
    
        # 寻找均值和方差
    
        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) )
    # 显示原始图像和Otsu's 二值化结果
    cv.imshow('Original Image', img)
    cv.imshow('Otsu Thresholding', otsu)
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    在这里插入图片描述

  • 相关阅读:
    web前-JAVA后端 数据API接口交互协议
    差分隐私(Differential Privacy)定义及其理解
    Spring-AOP底层原理
    上海交大牵手淘宝成立媒体计算实验室:推动视频超分等关键技术发展
    图论第一天|深度优先搜索理论基础、广度优先搜索理论基础、797.所有可能的路径
    Linux基本命令(RHCSA)超详解
    Zabbix“专家坐诊”第207期问答汇总
    MediatRPC - 基于MediatR和Quic通讯实现的RPC框架,比GRPC更简洁更低耦合,开源发布第一版
    Altium design 经验补充2
    Windows用户如何将cpolar内网穿透配置成后台服务,并开机自启动?
  • 原文地址:https://blog.csdn.net/weixin_47869094/article/details/134043255