查看全课大纲(第 6 / 93 节)
- 1.计算机视觉概述
- 2.计算机视觉应用场景
- 3.困难与挑战
- 4.开源库介绍
- 5.图像显示与存储
- 6.CLAHE
- 7.腐蚀膨胀与开闭运算
- 8.滤波与边缘填充
- 9.各种滤波
- 10.图像金字塔
- 11.傅里叶变换
- 12.短时傅里叶变换与小波变换
- 13.颜色特征与颜色直方图
- 14.几何特征_边缘
- 15.几何特征-角点与斑点
- 16.局部特征-SIFT特征描述子
- 17.局部特征-SURF特征描述子
- 18.局部特征-ORB特征描述子
- 19.其他特征描述子
- 20.传统图像分割技术
- 21.GraphCut
- 22.GrabCut
- 23.人脸检测的传统方法
- 24.行人检测的传统方法HOG+SVM
- 25.行人检测DPM
- 26.人工神经网络的架构
- 27.目标函数与梯度下降
- 28.误差反向传播
- 29.可手算的神经网络
- 30.改进目标函数与克服梯度消散
- 31.神经网络之传统神经网络与深度网络的差异
- 32.各种梯度下降算法
- 33.如何选择合适的梯度下降算法
- 34.避免过适应
- 35.CNN的架构
- 36.CNN的误差反向传播_卷积层
- 37.CNN的误差反向传播_池化层
- 38.分类所用的数据集
- 39.Alexnet详解
- 40.VGG详解
- 41.GoogLeNet_v1
- 42.BatchNormalization
- 43.GoogLeNet_v3v4
- 44.ResNet
- 45.ResNeXt
- 46.CNN设计准则
- 47.图像检索与推土机距离
- 48.基于颜色相似性
- 49.基于形状相似性
- 50.基于纹理相似性
- 51.基于局部特征相似性
- 52.DL的二值哈希编码
- 53.高维空间中检索KDTree
- 54.高维空间中检索LSH
- 55.目标识别任务综述
- 56.RCNN
- 57.SPP-Net
- 58.Fast R-CNN
- 59.Faster R-CNN
- 60.R-CNN系列模型回顾
- 61.R-FCN的设计思路
- 62.R-FCN的实现
- 63.YOLOv1
- 64.YOLOv2_9000
- 65.YOLOv3
- 66.图像分割应用场景与数据集
- 67.上采样的实现方式
- 68.FCN网络架构的由来
- 69.FCN构建细节
- 70.DeepLabv1
- 71.CRF
- 72.DeepLabv2
- 73.DeepLabv3
- 74.特定的医学影像分割任务
- 75.U-Net
- 76.3D U-Net
- 77.V-Net
- 78.DenseNet
- 79.病理切片分析任务及数据集
- 80.病理切片分析
- 81.图说任务及其传统处理方法
- 82.深度语言模型与RNN
- 83.LSTM及其变种
- 84.NIC模型
- 85.SAT模型
- 86.图说的性能指标
- 87.判别模型与生成模型
- 88.KL散度与JS散度
- 89.VAE
- 90.GAN的基本原理
- 91.DCGAN与CGAN
- 92.Wasserstein GAN
- 93.SRGAN
CLAHE
小象实战讲义 · 基于深度学习的计算机视觉
在图像预处理中,我们常常会遇到图像对比度不足、曝光不均或细节模糊的问题。本节将介绍一种强大的图像增强技术——限制对比度自适应直方图均衡(CLAHE)。学完本节,你将能够理解从基础的直方图均衡到其自适应、限制对比度版本的完整演进逻辑,并掌握使用OpenCV的cv2.createCLAHE接口来有效提升图像视觉质量的方法。
💡 核心导读
- 直方图与均衡化:理解图像直方图作为数据分布统计的本质,以及直方图均衡化(HE)通过拉伸累积分布函数来增强全局对比度的原理。
- 自适应均衡(AHE):认识全局HE在处理明暗不均图像时的局限,学习通过局部滑动窗口进行直方图均衡的自适应方法。
- 限制对比度(CLAHE):掌握CLAHE的核心创新——直方图修剪,理解其如何避免AHE放大均匀区域噪声的问题。
- 双线性插值:了解CLAHE中通过块间双线性插值来消除块状伪影,获得平滑、连续增强结果的关键步骤。
从直方图到直方图均衡化(HE)
我们首先明确所处的位置:在图像预处理的空间域处理中,点运算是指输出像素值仅依赖于输入像素自身信息的变换。全局直方图均衡化(HE)就是典型的点运算;而 AHE 与 CLAHE 在此基础上引入了局部窗口或分块,已经属于邻域处理的范畴(课程课件的分类图把 HE/CLAHE 都归在“点运算”分支;严格按“输出是否依赖邻域”划分,AHE/CLAHE 应算邻域处理,这里按后者表述)。
直方图是对图像数据特征分布的一种统计。对于单通道灰度图像,它统计的是每个灰度级(或区间,称为bin)出现的频数。例如,一张图像中很暗的背景会在低灰度值区间形成峰值,而较亮的物体则会在高灰度值区间有所体现。直方图直观地揭示了图像的对比度状况:如果所有像素的灰度都集中在某个狭窄的区间内(如0-50),图像就会显得暗淡、对比度低;反之,如果集中在高区间,图像则会过曝。
直方图均衡化(Histogram Equalization, HE) 的目标正是解决这种对比度不足的问题。其核心思想是:将原始集中在狭窄区间的灰度直方图,通过一种变换,“拉伸”到整个灰度范围(0-255)上,使得每个灰度级上的像素分布尽可能均匀。
这个变换的关键是累积分布函数(CDF)。CDF是直方图的累积和,它表示小于等于某一灰度级的像素总个数(除以总像素数即为其占比)。HE算法利用CDF作为映射函数:对于原始图像中的每一个灰度级 r,其变换后的新灰度级 s 为: 其中,M×N是图像总像素数,L是灰度级数(如256),CDF_min是累积分布的最小值。这个变换保证了映射函数是单调递增的,并且将值域映射到[0, L-1]。
理想情况下,变换后图像的CDF会接近一条从(0,0)到(L-1, 1)的直线,这意味着每个灰度级被分配到的像素数大致相等,图像的对比度得到了最大化。
然而,全局HE存在明显缺陷。当图像中同时存在非常亮和非常暗的区域时,全局均衡化可能会使亮部更亮、暗部更暗,导致局部细节丢失或过度增强,甚至在某些均匀区域(如天空、墙面)放大噪声。
import numpy as np
# 构造一个低对比度的小图像示例(6x6,灰度集中在100-150)
np.random.seed(42)
low_contrast_base = np.ones((6, 6), dtype=np.uint8) * 120 # 基础灰度120
# 添加一点细微变化
variation = np.array([[0, 5, 10, 15, 10, 5],
[5, 10, 15, 20, 15, 10],
[10, 15, 20, 25, 20, 15],
[15, 20, 25, 30, 25, 20],
[10, 15, 20, 25, 20, 15],
[5, 10, 15, 20, 15, 10]], dtype=np.uint8)
low_contrast_img = np.clip(low_contrast_base + variation, 100, 150)
print("原始低对比度图像 (6x6):")
print(low_contrast_img)
print("\n原始图像灰度范围:", low_contrast_img.min(), "~", low_contrast_img.max())原始低对比度图像 (6x6):
[[120 125 130 135 130 125]
[125 130 135 140 135 130]
[130 135 140 145 140 135]
[135 140 145 150 145 140]
[130 135 140 145 140 135]
[125 130 135 140 135 130]]
原始图像灰度范围: 120 ~ 150自适应直方图均衡化(AHE)
为了克服全局HE的局限性,自适应直方图均衡化(Adaptive Histogram Equalization, AHE) 被提出。AHE的核心思想是:不再对整个图像使用同一个变换,而是为每个像素点单独计算一个基于其局部邻域的变换函数。
一种经典的实现方式是使用滑动窗口。设定一个固定大小的窗口(例如,64x64像素),在图像上滑动:用窗口内的像素计算直方图和CDF,并对窗口内的像素做映射。由于窗口相互重叠,同一像素会被多个窗口映射多次,标准做法是取这些映射结果的平均值。也有更简单的实现只更新窗口中心像素(每个像素只被映射一次);本节示例代码为便于演示,采用后者。
AHE能很好地处理图像中局部对比度差异大的问题,例如在很暗的背景中有一小块亮部,AHE可以只增强该亮部所在区域的对比度,而不会影响到全局。
但是,AHE引入了一个新的问题:噪声放大。在图像中那些原本灰度分布很均匀的区域(如一面灰墙),其局部直方图本身就很集中。AHE强行对这些区域也进行均衡化拉伸,相当于把原本微小的噪声差异给放大了,导致均匀区域出现颗粒感或不必要的纹理。
限制对比度自适应直方图均衡化(CLAHE)
限制对比度自适应直方图均衡化(Contrast Limited Adaptive Histogram Equalization, CLAHE) 在AHE的基础上做出了关键改进,有效地抑制了噪声放大问题。
CLAHE的改进主要体现在两个步骤:直方图修剪和块间双线性插值。
1. 直方图修剪(Contrast Limiting)
这是CLAHE得名的关键。在计算每个图像块的直方图后,并不直接使用它,而是先设定一个对比度限制参数(clipLimit)。需要注意,OpenCV 中的 clipLimit 是一个无量纲倍数,实际计数阈值 = clipLimit ×(块内像素数 / 灰度级数),即“直方图平均高度的倍数”,而不是直接的计数值;clipLimit 常用 2.0~4.0。如果直方图中某个bin的计数值超过了这个实际阈值,就将超出的部分“剪掉”。
但剪掉的部分不能简单地丢弃,否则图像的总像素数会发生变化。因此,被剪掉的部分需要被均匀地重新分配到直方图的所有bin中。这个过程相当于对原始直方图进行了一次“平滑”或“平坦化”操作。
修剪后的直方图,其峰值被限制,避免了在均匀区域产生过于陡峭的CDF。CDF的变化更加平缓,从而使得灰度变换不会过于剧烈,抑制了噪声的过度放大。
2. 图像分块与块间双线性插值
CLAHE将图像划分为大小相等的矩形块(例如 8×8 个块)。对每个块,独立进行上述的直方图修剪与均衡化,并计算得到该块的灰度映射函数。
如果直接使用这些块映射函数(即每个块内的像素只使用本块的映射),会在块与块的边界处产生明显的、不连续的“块状”伪影。为了解决这个问题,CLAHE采用了双线性插值的方法来计算最终每个像素的灰度值。
插值规则如下(以分块网格为 M×N 为例):
- 块中心点:直接使用该块自身的映射函数计算。
- 块内部点(非边界):其灰度值由相邻的四个块(左上、右上、左下、右下)的映射函数,根据该点相对于这四个块中心的距离进行双线性插值得到。
- 图像边缘的点:可能只被两个或一个块影响,则使用相应的线性插值或直接映射。
具体而言,对于一个像素点,我们分别计算它在周围四个块的映射函数下对应的累积分布值(CDF值),然后对这些CDF值进行双线性插值,得到一个最终的CDF值,最后将这个CDF值映射回灰度级,作为该像素点的新灰度值。这个过程确保了增强后的图像是平滑且连续的。
import cv2
import numpy as np
# 使用OpenCV的CLAHE接口
# 注意:为了演示,我们使用上面构造的小图像,但CLAHE通常对更大图像效果明显。
# 由于图像太小,分块大小需要调整。
# 重新构造图像数据,确保自包含
demo_img = np.array([[120, 125, 130, 135, 130, 125],
[125, 130, 135, 140, 135, 130],
[130, 135, 140, 145, 140, 135],
[135, 140, 145, 150, 145, 140],
[130, 135, 140, 145, 140, 135],
[125, 130, 135, 140, 135, 130]], dtype=np.uint8)
# 创建一个CLAHE对象
# clipLimit: 对比度限制阈值(用于直方图修剪),通常2.0-4.0
# tileGridSize: 分块网格数量(行,列),例如(8,8)表示 8x8 个块;对6x6图像用(2,2),即每块3x3
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(2, 2))
# 应用CLAHE
clahe_result = clahe.apply(demo_img)
print("CLAHE处理后的图像 (6x6):")
print(clahe_result)
print("\nCLAHE处理后灰度范围:", clahe_result.min(), "~", clahe_result.max())CLAHE处理后的图像 (6x6):
[[ 85 113 165 184 147 85]
[113 170 193 212 175 142]
[165 193 217 225 198 165]
[184 212 225 227 215 184]
[147 175 198 215 179 147]
[ 85 142 165 184 147 113]]
CLAHE处理后灰度范围: 85 ~ 227从输出可以看到,CLAHE将原本集中在120-150的灰度,拉伸到了85~227的更大范围,显著提升了对比度。由于图像很小且结构简单,块效应不明显。
CLAHE算法步骤总结
- 图像分块:将输入图像划分为大小相同的矩形块(如 8×8 个块)。
- 块内处理:对每个块:
- a. 计算灰度直方图。
- b. 对直方图进行修剪(限制对比度)。
- c. 对修剪后的直方图进行均衡化,得到该块的灰度映射函数。
- 插值合成:对于图像中的每一个像素,根据其位置,选取周围相关块的映射函数,通过双线性插值计算其最终灰度值。
- (可选)混合:将CLAHE处理后的图像与原始图像按一定比例混合,可以使增强效果更柔和自然。
CLAHE的效果与应用
CLAHE在多个领域展现出优异的效果:
- 医学影像:增强X光、CT、MRI图像中的组织对比度,使血管、骨骼、病灶等细节更清晰。
- 水下摄影与去雾:有效改善因光线散射导致的对比度低下、颜色灰蒙问题,恢复图像色彩和细节。
- 遥感图像:增强地表特征的对比度,便于分析和识别。
- 监控视频:提升在背光或光照不均环境下拍摄的人脸、车牌等关键信息的可视性。
其“去雾”效果尤其令人印象深刻,这是因为雾气导致的图像对比度下降与灰度集中现象,恰好能被CLAHE的局部对比度增强机制有效改善(注意:CLAHE 提升的是对比度,并非真正估计并去除雾的物理模型)。
import numpy as np
# 演示CLAHE在模拟“块状噪声”图像上的优势(对比AHE的噪声放大问题)
# 构造一个包含均匀区域和细节区域的图像
np.random.seed(42)
base = np.ones((64, 64), dtype=np.uint8) * 100 # 均匀背景,灰度100
# 添加一些细节(一条渐变带)
for i in range(64):
base[20:40, i] = 100 + int(i * 0.8)
# 添加一些高斯噪声到整个图像
noise = np.random.normal(0, 3, (64, 64)).astype(np.int16)
simulated_img = np.clip(base + noise, 0, 255).astype(np.uint8)
# 应用HE(全局)
hist_global, _ = np.histogram(simulated_img.flatten(), 256, [0,256])
cdf_global = hist_global.cumsum()
cdf_global_normalized = (cdf_global - cdf_global.min()) * 255 / (cdf_global.max() - cdf_global.min())
cdf_global_normalized = cdf_global_normalized.astype('uint8')
he_result = cdf_global_normalized[simulated_img]
# 应用AHE(简易模拟,使用小窗口滑动,无对比度限制)
def simple_ahe(image, window_size=15):
pad = window_size // 2
image_padded = np.pad(image, pad, mode='reflect')
result = np.zeros_like(image)
h, w = image.shape
for i in range(h):
for j in range(w):
window = image_padded[i:i+window_size, j:j+window_size]
hist, _ = np.histogram(window.flatten(), 256, [0,256])
cdf = hist.cumsum()
cdf_normalized = (cdf - cdf.min()) * 255 / (cdf.max() - cdf.min())
cdf_normalized = cdf_normalized.astype('uint8')
result[i, j] = cdf_normalized[image[i, j]]
return result
# 注意:由于AHE计算较慢,这里仅打印其处理思想,不实际运行。
print("模拟图像已构造。")
print("均匀背景灰度~100,中间有一条从100到150左右的渐变带。")
print("若应用AHE(无限制对比度),均匀背景的噪声将被显著放大。")
print("而CLAHE通过直方图修剪,可以抑制这种噪声放大。")模拟图像已构造。
均匀背景灰度~100,中间有一条从100到150左右的渐变带。
若应用AHE(无限制对比度),均匀背景的噪声将被显著放大。
而CLAHE通过直方图修剪,可以抑制这种噪声放大。📝 动手练一练
- 累计直方图的作用:在直方图均衡化中,为什么我们使用累积分布函数(CDF)而不是直接使用直方图作为映射函数?CDF的哪两个数学特性保证了变换的合理性和有效性?
👉 点击查看参考答案
- 单调性:CDF是单调非递减函数。这保证了变换函数
s = T(r)也是单调的,即原始图像中较亮的像素在变换后仍然比较暗的像素亮,不会出现灰度级反转,保持了图像的基本亮度顺序。 - 值域控制:CDF的值域是
[0, 总像素数],通过简单的归一化(CDF - CDF_min) / (总像素数 - CDF_min),可以将其映射到[0, 1],再乘以(L-1)即可精确映射到目标灰度范围[0, L-1]内,避免了值域越界问题。 因此,CDF天然地满足直方图均衡化映射函数所需的条件:单调递增且值域可控。
- 限制对比度的意义:CLAHE中的“限制对比度”(直方图修剪)步骤,主要是为了解决AHE带来的什么问题?请描述修剪和重分配的过程是如何缓解这个问题的。
👉 点击查看参考答案
主要解决AHE在均匀区域放大噪声的问题。
- 问题根源:在图像灰度均匀的区域(如一面灰墙),局部直方图高度集中在一个或几个
bin上。AHE对此进行均衡化,会产生一个非常陡峭的CDF,导致微小的灰度差异(可能是噪声)被映射到很宽的灰度范围,从而视觉上放大了噪声,产生颗粒感。 - 修剪与重分配:CLAHE设定一个
clipLimit(实际阈值 =clipLimit× 块内像素数 / 灰度级数),将直方图中超过该阈值的部分裁剪掉。然后将裁剪掉的总像素数均匀地加回到直方图的所有bin中。这个过程相当于对尖锐的直方图进行了“平滑”或“压低峰值”的操作。 - 效果:修剪重分配后的直方图更加平坦,对应的CDF曲线变化更加平缓。使用这个平缓的CDF进行映射,灰度变换就不会那么剧烈,原本均匀区域内的微小噪声差异被映射到的灰度范围变窄,从而抑制了噪声的视觉放大效果,使得增强结果更自然。
本章小结
本节我们深入学习了图像增强中一项经典而强大的技术——限制对比度自适应直方图均衡化(CLAHE)。我们从基础的直方图和全局直方图均衡化(HE)出发,理解了其增强对比度的原理与局限。进而,我们学习了自适应直方图均衡化(AHE)如何通过局部处理来改善全局HE的不足,但也认识了其放大噪声的新问题。最后,我们掌握了CLAHE通过直方图修剪来限制对比度、以及通过块间双线性插值来保证结果平滑连续的核心机制,这使得CLAHE成为处理光照不均、低对比度图像的实用且有效的工具。
📋 行动清单
- 使用OpenCV的
cv2.createCLAHE()函数,对一张你自己选择的低对比度图片进行处理,尝试调整clipLimit和tileGridSize参数,观察它们对最终增强效果的影响。 - 思考并比较:在深度学习时代,像CLAHE这样的传统图像预处理技术,在训练视觉模型(如分类、检测网络)的数据增强流水线中,可能扮演什么角色?是必须的,还是可以被网络自身的学习能力所替代?
- (选做)尝试不借助OpenCV,用NumPy手动实现一个简化版的CLAHE算法(例如,忽略双线性插值,只实现分块和限制对比度的均衡),加深对算法每一步的理解。
—— 小象教研组