小杰机器视觉(five day)——直方图均衡化

发布于:2025-09-04 ⋅ 阅读:(17) ⋅ 点赞:(0)

1.直方图均衡化

(1)概念

        一张好的图像通常在直方图上分布比较均匀,这样的图像对比度比较强烈(明暗差别大)。直方图均衡化可以提升原图的观感,需要遍历图像的像素,统计每个灰度值出现的频数、比例与累积比例,并重新映射到新的范围(如0-255,或其他范围),从而提升图像的观感。

均衡化之后的图像显示效果会更加强烈,具有广泛地应用前景:

  • 医学影像处理

X光、CT扫描、核磁共振

  • 摄影与监控

改善照片的对比度和色彩平衡提升观感

  • 卫星影像处理

可以增加图像质量,提高解析度

(2)原始直方图均衡化

        可以通过虚拟仿真实验的直方图均衡化节点,选择hist_method(直方图均衡化方法)中的equalizeHist参数设置为原始的直方图均衡化算法。

        通过调整图像像素值的分布,使图像的对比度和亮度得到改善。

算法过程如下:

统计数据→

        假设有一个3*3的图像,其灰度如上图所示,先统计每个像素值的频数、比例与累计比例,统计完成后,代入下面的公式:

新的像素值 = 要缩放的范围(通常为0-255)* 该像素的累计比例

        比如灰度为50的像素点,范围255*累积比例0.33=84.15,取整后为84,使用灰度值84替换原图中50灰度值的像素点。原始图像src中所有的灰度值都带入以上公式,算出它们在目标图像dst中的新的灰度值,如下图所示。

        代码

import cv2
import numpy as np


# 直方图绘制函数
def draw_hist(image, color):
    """
    绘制直方图
    :param image: 哪个图的直方图图像(灰度化之后的)
    :param color: 直方图柱子什么颜色,BGR元组
    :return: 直方图图像
    """
    hist = cv2.calcHist(
        [image],  # 计算直方图的图像,支持多图像输入,因此一个图像也要写成列表
        [0],  # 要计算的图像灰度值通道需要,灰度图直接传0
        None,  # 掩膜,全图计算不需要
        [256],  # 直方图x轴的精细程度,256表示分为256份
        [0, 255]  # x轴的范围
    )
    print(hist.shape)  # (256, 1) 256表示有256个灰度频数数据
    # 提取关键数据
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(hist)
    # print('min_val:', min_val)  # 0.0,最小的灰度频数
    print('max_val:', max_val)  # 471606.0,最大的灰度频数
    # print('min_loc:', min_loc)  # (0, 255),最小的灰度频数对应的灰度值
    # print('max_loc:', max_loc)  # (0, 1),最大的灰度频数对应的灰度值
    # 创建一张纯黑图,画柱子
    hist_img = np.zeros([256, 256, 3], np.uint8)
    # 为了让y轴最高的柱子留一部门空白,最高柱子的高度
    hpt = int(256 * 0.9)
    for h in range(256):  # 从0到255,每个灰度画柱子
        # 柱子高度(整数) = 直方图的最高值hpt * 每个灰度的频数/最高的频数
        intensity = int(hpt * hist[h] / max_val)
        # print(intensity)
        # 画柱子(线)
        cv2.line(
            hist_img,
            (h, 256),  # x轴的起始点
            (h, 256 - intensity),  # 线段从下往上画的终点
            color
        )
    return hist_img


if __name__ == '__main__':
    # 1. 图片输入
    path = 'ct.png'
    image_np = cv2.imread(path)

    # 2. 灰度化
    image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)

    # 3. 绘制直方图
    color = {
        'red': (0, 0, 255),
        'green': (0, 255, 0),
        'blue': (255, 0, 0)
    }
    hist_img = draw_hist(image_np_gray, color['green'])

    # 4. 直方图均衡化
    equ_hist_image_np = cv2.equalizeHist(image_np_gray)

    # 5. 绘制直方图
    eqi_hist_img = draw_hist(equ_hist_image_np, color['red'])

    # 6. 图片输出
    cv2.imshow('hist_img', hist_img)
    cv2.imshow('eqi_hist_img', eqi_hist_img)
    cv2.imwrite('equ_hist_image_np.jpg', equ_hist_image_np)
    cv2.waitKey(0)
        该算法适用于图像的灰度分布不均匀且灰度分布较窄的图像,可以提升图像的细节和对比度。但是,传统的直方图均衡化方法会引入噪声,并导致图像中出现过度增强的区域。这是没有考虑到图像的局部特征和全图对比度的差异导致的。

(3)自适应均衡化

        在直方图均值化节点中,选择hist_method为createCLAHE(自适应均衡化,对比度受限的自适应均衡化)方法。

        这种方法可以在小区域(8*8)内进行直方图均衡化,因此将对比度进行了限制。如果小区域内有噪声,也会被放大,需要对小区域进行对比度限制。

主要步骤如下:

1. 将图像分为大小相同的矩形块,块分的越细,相对效果越好。

2. 统计每个块的直方图信息,并生成每个块单独的直方图H。

3. 根据设置的限制参数强度,对直方图信息重新分配。

(a)设定一个阈值T,将均衡化后的直方图从H[0]到H[255](灰度0-255,即x轴)上依次遍历,把大于阈值T的都全部减去,并统计一共减去了多少个像素。

(b)将减去的像素值的和除以缩放的范围(256),添加到每个灰度值的频数。

4. 重复2、3步骤,直到全图所有的块计算完成。

5. 为了再次减少块状效应,对每个小块进行双线性插值。

注意:

  • 把原始图像分为小块处理时,如果不是整数倍,默认内部使用BORDER_REFLECT_101进行边界填充,以此来保证分割的是整数倍。
  • 双线性插值计算不公平问题

(a)对于四个角点(红色),直接使用均衡化之后的像素值。

(b)对于在frame上的像素点(绿色),进行单线性插值。

(c)其他情况的像素点(蓝色),进行双线性插值。

下面是自适应均衡化的相关参数:

  • clipLimit

对比度限制阈值

  • tileGridSize

小区块范围,推荐8*8

        代码

import cv2
import numpy as np


# 直方图绘制函数
def draw_hist(image, color):
    """
    绘制直方图
    :param image: 哪个图的直方图图像(灰度化之后的)
    :param color: 直方图柱子什么颜色,BGR元组
    :return: 直方图图像
    """
    hist = cv2.calcHist(
        [image],  # 计算直方图的图像,支持多图像输入,因此一个图像也要写成列表
        [0],  # 要计算的图像灰度值通道需要,灰度图直接传0
        None,  # 掩膜,全图计算不需要
        [256],  # 直方图x轴的精细程度,256表示分为256份
        [0, 255]  # x轴的范围
    )
    print(hist.shape)  # (256, 1) 256表示有256个灰度频数数据
    # 提取关键数据
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(hist)
    # print('min_val:', min_val)  # 0.0,最小的灰度频数
    print('max_val:', max_val)  # 471606.0,最大的灰度频数
    # print('min_loc:', min_loc)  # (0, 255),最小的灰度频数对应的灰度值
    # print('max_loc:', max_loc)  # (0, 1),最大的灰度频数对应的灰度值
    # 创建一张纯黑图,画柱子
    hist_img = np.zeros([256, 256, 3], np.uint8)
    # 为了让y轴最高的柱子留一部门空白,最高柱子的高度
    hpt = int(256 * 0.9)
    for h in range(256):  # 从0到255,每个灰度画柱子
        # 柱子高度(整数) = 直方图的最高值hpt * 每个灰度的频数/最高的频数
        intensity = int(hpt * hist[h] / max_val)
        # print(intensity)
        # 画柱子(线)
        cv2.line(
            hist_img,
            (h, 256),  # x轴的起始点
            (h, 256 - intensity),  # 线段从下往上画的终点
            color
        )
    return hist_img


if __name__ == '__main__':
    # 1. 图片输入
    path = 'ct.png'
    image_np = cv2.imread(path)

    # 2. 灰度化
    image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)

    # 3. 绘制直方图
    color = {
        'red': (0, 0, 255),
        'green': (0, 255, 0),
        'blue': (255, 0, 0)
    }
    hist_img = draw_hist(image_np_gray, color['green'])

    # 4. 直方图均衡化
    # equ_hist_image_np = cv2.equalizeHist(image_np_gray)

    # 创建自适应直方图均衡化的参数对象
    clahe = cv2.createCLAHE(
        clipLimit=3,  # 对比度限制阈值
        tileGridSize=(80, 80)  # 小区域面积
    )
    # 自适应直方图均衡化
    equ_hist_image_np = clahe.apply(image_np_gray)

    # 5. 绘制直方图
    eqi_hist_img = draw_hist(equ_hist_image_np, color['red'])

    # 6. 图片输出
    cv2.imshow('hist_img', hist_img)
    cv2.imshow('eqi_hist_img', eqi_hist_img)
    cv2.imwrite('equ_hist_image_np3.jpg', equ_hist_image_np)
    cv2.waitKey(0)

        效果

原图

直方图均衡化

自适应直方图均衡化