2. 直方图均衡化原理
直方图均衡化,说白了就是让一张图的亮度分布更均匀。我刚开始接触这个概念时,觉得它就是个数学公式套用。后来做项目才发现,理解它背后的逻辑,比背公式重要得多。
咱们先想一个问题:为什么有些图看起来灰蒙蒙的?因为它的像素值挤在了一个小范围内。比如一张夜景照片,大部分像素都集中在暗部。均衡化要做的,就是把这些挤在一起的像素“拉开”,让它们均匀分布在整个亮度区间里。
均衡化的数学推导
先定义几个基本符号。假设一张图的灰度级是 [0, L-1],比如 8 位图就是 0 到 255。
- r:输入图像的灰度值
- s:输出图像的灰度值
- p_r(r):输入图像的概率密度函数(PDF)
- p_s(s):输出图像的概率密度函数
我们希望找到一个变换函数 s = T(r),让 p_s(s) 变成均匀分布。这个变换函数长什么样?
核心公式:
s = T(r) = (L-1) ∫₀ʳ p_r(w) dw
对于离散图像,积分变成求和:
s_k = (L-1) ∑ⱼ₌₀ᵏ p_r(r_j) = (L-1) ∑ⱼ₌₀ᵏ (n_j / N)
其中 n_j 是灰度级 r_j 的像素个数,N 是总像素数。
这个公式怎么来的?我简单解释一下。概率论里有个结论:如果一个随机变量 r 经过某个单调递增函数 T 变换后得到 s,那么 s 的 PDF 和 r 的 PDF 之间有关系:
p_s(s) = p_r(r) · |dr/ds|
把 s = T(r) 代进去,再让 p_s(s) = 1/(L-1)(均匀分布),就能推导出上面的积分公式。嗯,这里要注意,T(r) 必须是单调递增的,否则会出现灰度反转的问题。
累积分布函数(CDF)的作用
你发现没有?上面公式里的 ∑ⱼ₌₀ᵏ p_r(r_j) 其实就是累积分布函数(CDF)。
| 概念 | 公式 | 含义 |
|---|---|---|
| 概率密度函数(PDF) | p_r(r_k) = n_k / N | 每个灰度级出现的概率 |
| 累积分布函数(CDF) | cdf(k) = ∑ⱼ₌₀ᵏ p_r(r_j) | 灰度 ≤ k 的像素占比 |
CDF 在这里扮演了什么角色?我打个比方。假设你有一堆数据,你想把它们映射到 0 到 255 之间,而且希望映射后的数据分布均匀。CDF 天然就是 0 到 1 之间的单调递增函数,用它做映射,正好能把密集的区域“拉伸”,稀疏的区域“压缩”。
我的经验: 在实际项目中,我经常用 CDF 来检查均衡化的效果。如果均衡化后的 CDF 接近一条直线,说明效果很好。如果出现明显的阶梯状,说明灰度级合并得太严重了。
为什么会这样?因为 CDF 的斜率就是 PDF。CDF 越陡,说明该区域的像素越密集。均衡化后,CDF 变成直线,意味着 PDF 是常数——也就是均匀分布。
均衡化的效果分析
均衡化到底能带来什么效果?我拿一个实际场景来说。有一次我做医学图像处理,一张 X 光片曝光不足,骨骼和软组织的对比度很低。均衡化之后,细节一下子就出来了。
但均衡化不是万能的。我总结了几点:
- 对比度提升:这是最直接的效果。原本挤在一起的灰度级被拉开,图像看起来更清晰。
- 灰度级合并:这是副作用。因为映射是离散的,多个输入灰度级可能映射到同一个输出灰度级,导致细节丢失。
- 噪声放大:如果原图有噪声,均衡化会把噪声也“拉伸”,让噪声更明显。
- 亮度漂移:均衡化不保留平均亮度。一张暗图均衡化后可能变亮,一张亮图可能变暗。
避坑指南: 我曾经在一个项目中直接用全局均衡化处理一张有雾的图像,结果天空区域出现了明显的伪影。后来改用限制对比度的自适应直方图均衡化(CLAHE),才解决了这个问题。所以,均衡化不是万能的,要根据场景选择合适的方法。
为了更直观地理解均衡化的过程,我画了一张流程图:
最后,我再说一个实际案例。有一次处理卫星遥感图像,原图因为大气散射,整体偏灰。均衡化后,地物边界变得清晰,但水体区域出现了明显的颗粒感。这是因为水体区域的像素本来就少,均衡化把它们拉伸后,量化误差被放大了。
所以,均衡化是一把双刃剑。用得好,效果立竿见影;用得不好,反而会引入 artifacts。我的建议是:先看直方图,再决定是否均衡化。如果直方图已经覆盖了整个灰度范围,只是分布不均匀,那均衡化的效果可能不明显。如果直方图集中在一个小区域,那均衡化就是首选方案。