• 【OpenCV】Chapter4.灰度变换与直方图


    最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。
    【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.blog.csdn.net/article/details/125112487
    程序仓库:https://github.com/zstar1003/OpenCV-Learning

    图像二值化

    二值图像指的是只有黑色和白色两种颜色的图像。每个像素点可以用 0/1 表示,0 表示黑色,1 表示白色。
    OpenCV提供了cv2.threshold,可以对图像进行二值化处理。

    cv2.threshold(src, thresh, maxval, type[, dst]) → retval, dst

    参数说明:

    • scr:变换操作的输入图像,nparray 二维数组,必须是单通道灰度图像!
    • thresh:阈值,取值范围 0~255
    • maxval:填充色,取值范围 0~255,一般取 255
    • type:变换类型
      • cv2.THRESH_BINARY:大于阈值时置 255,否则置 0
      • cv2.THRESH_BINARY_INV:大于阈值时置 0,否则置 255
      • cv2.THRESH_TRUNC:大于阈值时置为阈值 thresh,否则不变(保持原色)
      • cv2.THRESH_TOZERO:大于阈值时不变(保持原色),否则置 0
      • cv2.THRESH_TOZERO_INV:大于阈值时置 0,否则不变(保持原色)
      • cv2.THRESH_OTSU:使用 OTSU 算法选择阈值
    • 返回值 retval:返回二值化的阈值
    • 返回值 dst:返回阈值变换的输出图像

    示例程序:

    """
    图像二值化
    """
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    
    
    imgGray = cv2.imread("../img/img.jpg", flags=0)  # flags=0 读取为灰度图像
    
    ret1, img1 = cv2.threshold(imgGray, 63, 255, cv2.THRESH_BINARY)  # 转换为二值图像, thresh=63
    ret2, img2 = cv2.threshold(imgGray, 127, 255, cv2.THRESH_BINARY)  # 转换为二值图像, thresh=127
    ret3, img3 = cv2.threshold(imgGray, 191, 255, cv2.THRESH_BINARY)  # 转换为二值图像, thresh=191
    ret4, img4 = cv2.threshold(imgGray, 127, 255, cv2.THRESH_BINARY_INV)  # 逆二值图像,BINARY_INV
    ret5, img5 = cv2.threshold(imgGray, 127, 255, cv2.THRESH_TRUNC)  # TRUNC 阈值处理,THRESH_TRUNC
    ret6, img6 = cv2.threshold(imgGray, 127, 255, cv2.THRESH_TOZERO)  # TOZERO 阈值处理,THRESH_TOZERO
    
    plt.figure(figsize=(9, 6))
    titleList = ["1. BINARY(thresh=63)", "2. BINARY(thresh=127)", "3. BINARY(thresh=191)", "4. THRESH_BINARY_INV",
                 "5. THRESH_TRUNC", "6. THRESH_TOZERO"]
    imageList = [img1, img2, img3, img4, img5, img6]
    for i in range(6):
        plt.subplot(2, 3, i + 1), plt.title(titleList[i]), plt.axis('off')
        plt.imshow(imageList[i], 'gray')  # 灰度图像 ndim=2
    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

    在这里插入图片描述

    反色变换

    图像的反色变换,即图像反转,将黑色像素点变白色,白色像素点变黑色。广义的反色变换也可以应用于彩色图像,即对所有像素点取补。

    反色变换实现起来比较简单,遍历每个像素点,取反即可。

    """
    反色变换
    """
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    
    
    img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
    imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 颜色转换:BGR(OpenCV) -> Gray
    h, w = img.shape[:2]  # 图片的高度和宽度
    
    imgInv = np.empty((w, h), np.uint8)  # 创建空白数组
    for i in range(h):
        for j in range(w):
            imgInv[i][j] = 255 - imgGray[i][j]
    
    plt.figure(figsize=(10, 6))
    plt.subplot(131), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("imgBGR"), plt.axis('off')
    plt.subplot(132), plt.imshow(imgGray, cmap='gray'), plt.title("imgGray"), plt.axis('off')
    plt.subplot(133), plt.imshow(imgInv, cmap='gray'), plt.title("imgInv"), plt.axis('off')
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    灰度线性变换

    灰度线性变换主要是指对图像的每一个像素作线性拉伸,可以凸显图像的细节,提高图像的对比度。
    变换公式:
    在这里插入图片描述
    式中,D 为原始图像的灰度值,Dt 为线性灰度变换后的图像灰度值。

    • 当 α = 1 , β = 0时,保持原始图像不变
    • 当 α = 1 , β > 0时,图像的灰度值上移,灰度图像颜色发白(彩色图像颜色发亮)
    • 当 α = 1 , β < 0时,图像的灰度值下移,灰度图像颜色发黑(彩色图像颜色发暗)
    • 当 α > 1 时,图像的对比度增强
    • 当 0 < α < 1 时,图像的对比度减小
    • 当 α < 0 , β = 255时,图像暗区域变亮,亮区域变暗,图像求补
    • 当 α = − 1 , β = 255时,图像的灰度值反转

    示例程序:

    """
    灰度线性变换
    """
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    
    img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
    imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 颜色转换:BGR(OpenCV) -> Gray
    h, w = img.shape[:2]  # 图片的高度和宽度
    img1 = np.empty((w, h), np.uint8)  # 创建空白数组
    img2 = np.empty((w, h), np.uint8)  # 创建空白数组
    img3 = np.empty((w, h), np.uint8)  # 创建空白数组
    img4 = np.empty((w, h), np.uint8)  # 创建空白数组
    img5 = np.empty((w, h), np.uint8)  # 创建空白数组
    img6 = np.empty((w, h), np.uint8)  # 创建空白数组
    
    # Dt[i,j] = alfa*D[i,j] + beta
    alfa1, beta1 = 1, 50  # alfa=1,beta>0: 灰度值上移
    alfa2, beta2 = 1, -50  # alfa=1,beta<0: 灰度值下移
    alfa3, beta3 = 1.5, 0  # alfa>1,beta=0: 对比度增强
    alfa4, beta4 = 0.75, 0  # 0
    alfa5, beta5 = -0.5, 0  # alfa<0,beta=0: 暗区域变亮,亮区域变暗
    alfa6, beta6 = -1, 255  # alfa=-1,beta=255: 灰度值反转
    
    for i in range(h):
        for j in range(w):
            img1[i][j] = min(255, max((imgGray[i][j] + beta1), 0))  # alfa=1,beta>0: 颜色发白
            img2[i][j] = min(255, max((imgGray[i][j] + beta2), 0))  # alfa=1,beta<0: 颜色发黑
            img3[i][j] = min(255, max(alfa3 * imgGray[i][j], 0))  # alfa>1,beta=0: 对比度增强
            img4[i][j] = min(255, max(alfa4 * imgGray[i][j], 0))  # 0
            img5[i][j] = alfa5 * imgGray[i][j] + beta5  # alfa<0,beta=255: 暗区域变亮,亮区域变暗
            img6[i][j] = min(255, max(alfa6 * imgGray[i][j] + beta6, 0))  # alfa=-1,beta=255: 灰度值反转
    
    plt.figure(figsize=(10, 6))
    titleList = ["1. imgGray", "2. beta=50", "3. beta=-50", "4. alfa=1.5", "5. alfa=0.75", "6. alfa=-0.5"]
    imageList = [imgGray, img1, img2, img3, img4, img5]
    for i in range(6):
        plt.subplot(2, 3, i + 1), plt.title(titleList[i]), plt.axis('off')
        plt.imshow(imageList[i], vmin=0, vmax=255, cmap='gray')
    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

    在这里插入图片描述

    伽马变换

    伽马变换也称幂律变换,可以提升暗部细节,对发白(曝光过度)或过暗(曝光不足)的图片进行矫正。
    公式:
    在这里插入图片描述

    • 0 < γ < 1 0< \gamma <1 0<γ<1 时,拉伸图像中灰度级较低的区域,压缩灰度级较高的部分,增加图像的对比度
    • γ > 1 \gamma >1 γ>1时,拉伸图像中灰度级较高的区域,压缩灰度级较低的部分,降低图像的对比度。

    示例程序:

    """
    伽马变换
    """
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    
    
    img = cv2.imread("../img/img.jpg", flags=0)  # flags=0 读取为灰度图像
    gammaList = [0.125, 0.25, 0.5, 1.0, 2.0, 4.0]  # gamma 值
    normImg = lambda x: 255. * (x - x.min()) / (x.max() - x.min() + 1e-6)  # 归一化为 [0,255]
    
    plt.figure(figsize=(9, 6))
    for k in range(len(gammaList)):
        imgGamma = np.power(img, gammaList[k])
        imgGamma = np.uint8(normImg(imgGamma))
    
        plt.subplot(2, 3, k + 1), plt.axis('off')
        plt.imshow(imgGamma, cmap='gray', vmin=0, vmax=255)
        plt.title(f"$\gamma={gammaList[k]}$")
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    灰度直方图

    灰度直方图是反映图像像素分布的统计图,横坐标代表像素值的取值区间,纵坐标代表每一像素值在图像中的像素总数或者所占的百分比。

    灰度图分布居中说明亮度正常,偏左说明亮度较暗,偏右表明亮度较高;狭窄陡峭表明对比度较低,宽泛平缓表明对比度较高。

    有两种方式可以灰度直方图,第一种方式是使用OpenCV提供的cv2.calcHist函数,另一种方式是使用Numpy的np.histogram函数。

    示例程序:

    """
    灰度直方图
    """
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    
    img = cv2.imread("../img/lena.jpg", flags=0)  # flags=0 读取为灰度图像
    
    histCV = cv2.calcHist([img], [0], None, [256], [0, 256])  # OpenCV 函数 cv2.calcHist
    histNP, bins = np.histogram(img.flatten(), 256)
    
    plt.figure(figsize=(10, 3))
    plt.subplot(131), plt.imshow(img, cmap='gray', vmin=0, vmax=255), plt.title("Original"), plt.axis('off')
    plt.subplot(132, xticks=[], yticks=[]), plt.axis([0, 255, 0, np.max(histCV)])
    plt.bar(range(256), histCV[:, 0]), plt.title("Gray Hist(cv2.calcHist)")
    plt.subplot(133, xticks=[], yticks=[]), plt.axis([0, 255, 0, np.max(histCV)])
    plt.bar(bins[:-1], histNP), plt.title("Gray Hist(np.histogram)")
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    直方图均衡化

    直方图均衡化的基本思想是对图像中占比大的灰度级进行展宽,而对占比小的灰度级进行压缩,使图像的直方图分布较为均匀,扩大灰度值差别的动态范围,从而增强图像整体的对比度。

    OpenCV 提供了函数cv2.equalizeHist可以实现直方图均衡化。

    示例代码:

    """
    直方图均衡
    """
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    
    
    img = cv2.imread("../img/lena.jpg", flags=0)  # flags=0 读取为灰度图像
    
    imgEqu = cv2.equalizeHist(img)  # 使用 cv2.qualizeHist 完成直方图均衡化变换
    
    fig = plt.figure(figsize=(7, 7))
    plt.subplot(221), plt.title("Original image (youcans)"), plt.axis('off')
    plt.imshow(img, cmap='gray', vmin=0, vmax=255)  # 原始图像
    plt.subplot(222), plt.title("Hist-equalized image"), plt.axis('off')
    plt.imshow(imgEqu, cmap='gray', vmin=0, vmax=255)  # 转换图像
    histImg, bins = np.histogram(img.flatten(), 256)  # 计算原始图像直方图
    plt.subplot(223, yticks=[]), plt.bar(bins[:-1], histImg)  # 原始图像直方图
    plt.title("Histogram of original image"), plt.axis([0, 255, 0, np.max(histImg)])
    histEqu, bins = np.histogram(imgEqu.flatten(), 256)  # 计算原始图像直方图
    plt.subplot(224, yticks=[]), plt.bar(bins[:-1], histEqu)  # 转换图像直方图
    plt.title("Histogram of equalized image"), plt.axis([0, 255, 0, np.max(histImg)])
    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

    在这里插入图片描述

    直方图规定化

    直方图规定化,是指将图像的直方图调整为规定的形状。 例如,将一幅图像或某一区域的直方图匹配到另一幅影像上,使两幅影像的色调保持一致。

    示例程序:

    """
    直方图规定化
    """
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    
    
    img = cv2.imread("../img/lena.jpg", flags=0)  # flags=0 读取为灰度图像
    imgRef = cv2.imread("../img/mb.jpg", flags=0)  # 匹配模板图像, matching template
    
    # 计算累计直方图
    histImg, bins = np.histogram(img.flatten(), 256)  # 计算原始图像直方图
    histRef, bins = np.histogram(imgRef.flatten(), 256)  # 计算匹配模板直方图
    cdfImg = histImg.cumsum()  # 计算原始图像累积分布函数 CDF
    cdfRef = histRef.cumsum()  # 计算匹配模板累积分布函数 CDF
    
    # 计算直方图匹配转换函数
    transM = np.zeros(256)
    for i in range(256):
        index = 0
        vMin = np.fabs(cdfImg[i] - cdfRef[0])
        for j in range(256):
            diff = np.fabs(cdfImg[i] - cdfRef[j])
            if (diff < vMin):
                index = int(j)
                vMin = diff
        transM[i] = index
    
    
    imgOut = transM[img].astype(np.uint8)
    
    fig = plt.figure(figsize=(10, 7))
    plt.subplot(231), plt.title("Original image"), plt.axis('off')
    plt.imshow(img, cmap='gray')  # 原始图像
    plt.subplot(232), plt.title("Matching template"), plt.axis('off')
    plt.imshow(imgRef, cmap='gray')  # 匹配模板
    plt.subplot(233), plt.title("Matching output"), plt.axis('off')
    plt.imshow(imgOut, cmap='gray')  # 匹配结果
    histImg, bins = np.histogram(img.flatten(), 256)  # 计算原始图像直方图
    plt.subplot(234, yticks=[]), plt.bar(bins[:-1], histImg)
    histRef, bins = np.histogram(imgRef.flatten(), 256)  # 计算匹配模板直方图
    plt.subplot(235, yticks=[]), plt.bar(bins[:-1], histRef)
    histOut, bins = np.histogram(imgOut.flatten(), 256)  # 计算匹配结果直方图
    plt.subplot(236, yticks=[]), plt.bar(bins[:-1], histOut)
    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

    在这里插入图片描述

    局部直方图均衡化

    局部直方图处理的过程是:

    (1)设定某一大小的模板(矩形邻域),在图像中沿逐个像素移动;

    (2)对每个像素位置,计算模板区域的直方图,对该局部区域进行直方图均衡或直方图匹配变换,变换结果只用于模板区域中心像素点的灰度值修正;

    (3)模板(邻域)在图像中逐行逐列移动,遍历所有像素点,完成对整幅图像的局部直方图处理。

    OpenCV 提供了类cv2.createCLAHE用于创建自适应均衡化的对象和方法,可以实现局部直方图处理。

    cv2.createCLAHE([, clipLimit[, tileGridSize]]) → retval

    参数说明:

    • clipLimit:颜色对比度的阈值,可选项,默认值 8
    • titleGridSize:局部直方图均衡化的模板(邻域)大小,可选项,默认值 (8,8)

    示例程序:

    """
    局部直方图均衡化
    """
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    
    
    img = cv2.imread("../img/lena.jpg", flags=0)  # flags=0 读取为灰度图像
    
    imgEqu = cv2.equalizeHist(img)  # 全局直方图均衡化
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(4, 4))  # 创建 CLAHE 对象
    imgLocalEqu = clahe.apply(img)  # 自适应的局部直方图均衡化
    
    plt.figure(figsize=(9, 6))
    plt.subplot(131), plt.title('Original'), plt.axis('off')
    plt.imshow(img, cmap='gray', vmin=0, vmax=255)
    plt.subplot(132), plt.title(f'Global Equalize Hist'), plt.axis('off')
    plt.imshow(imgEqu, cmap='gray', vmin=0, vmax=255)
    plt.subplot(133), plt.title(f'Local Equalize Hist'), plt.axis('off')
    plt.imshow(imgLocalEqu, cmap='gray', vmin=0, vmax=255)
    plt.tight_layout()
    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

    在这里插入图片描述

  • 相关阅读:
    Python Django 详解(基础)
    为什么我的结巴分词和词云图不报错但是乱码啊
    MTK6877/MT6877天玑900安卓5G核心板_安卓开发板主板定制开发
    2020 MIT6.s081 Lab: xv6 lazy page allocation
    编译添加了ALPHA开发板的NXP官方uboot
    bash和sh和./的区别
    Object.defineProperty 与 Proxy 有什么区别?
    centos7.9部署nexus内网源服务器(yum,apt)
    速看,成都市关于促进低效工业用地企业提质增效的信息
    RequestParam,RequestBody,PathVariable等参数绑定注解
  • 原文地址:https://blog.csdn.net/qq1198768105/article/details/126353283