2、直方图均衡化(HE):原理讲解、全局直方图均衡化实现、局限性分析
直方图均衡化,英文叫 Histogram Equalization,简称 HE。这名字听起来挺唬人,说白了就是「把图像的灰度分布拉均匀」。我刚开始接触图像处理时,觉得这玩意儿就是调对比度,后来踩了坑才发现——远没那么简单。
2.1 直方图均衡化的核心原理
先问一个问题:一张低光照片,它的直方图长什么样?
答案是——像素值都挤在暗区,比如 0~50 这个范围。亮区几乎没数据。整张图看起来灰蒙蒙的,细节全藏在暗部里。
直方图均衡化的思路很直接:把挤在一起的像素值,重新映射到整个 0~255 范围。让暗的更暗?不对。是让暗部的细节能拉开,亮部的细节也能保留。
核心公式(理解即可,不用背):
新像素值 = (L - 1) × 累积分布函数(原像素值)
其中 L 是灰度级数,8位图就是 256。累积分布函数(CDF)就是把直方图从左边累加到右边。
举个例子。一张图只有 4 个像素:0、0、1、2。直方图是:0 出现 2 次,1 出现 1 次,2 出现 1 次。CDF 算出来:0→2,1→3,2→4。映射后:0→0,1→85,2→170。你看,原本挤在一起的 0 和 1,被拉开了。
嗯,原理就这么简单。但实际做起来,坑不少。
2.2 全局直方图均衡化实现
我习惯用 OpenCV 做快速验证。代码不长,但每一步都有讲究。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取低光图像
img = cv2.imread('low_light.jpg', cv2.IMREAD_GRAYSCALE)
# 全局直方图均衡化
equ = cv2.equalizeHist(img)
# 显示结果
plt.figure(figsize=(12, 6))
plt.subplot(2, 2, 1), plt.imshow(img, cmap='gray'), plt.title('原图')
plt.subplot(2, 2, 2), plt.imshow(equ, cmap='gray'), plt.title('HE 结果')
plt.subplot(2, 2, 3), plt.hist(img.ravel(), 256, [0, 256]), plt.title('原图直方图')
plt.subplot(2, 2, 4), plt.hist(equ.ravel(), 256, [0, 256]), plt.title('HE 直方图')
plt.tight_layout()
plt.show()
你看,核心就一行:cv2.equalizeHist()。但这一行背后,OpenCV 帮你做了三件事:统计直方图、计算 CDF、做映射。
我的习惯:做 HE 之前,先看一眼直方图。如果直方图是「单峰且集中在暗区」,HE 效果通常不错。如果直方图已经比较均匀了,HE 反而可能破坏原有风格。
我自己写过一个纯 Python 的实现,方便理解内部逻辑:
def my_equalize_hist(img):
# 1. 统计直方图
hist = np.zeros(256, dtype=np.int32)
for pixel in img.ravel():
hist[pixel] += 1
# 2. 计算累积分布函数
cdf = hist.cumsum()
cdf_normalized = cdf * 255 / cdf[-1] # 归一化到 0~255
# 3. 映射
equ = np.interp(img.ravel(), np.arange(256), cdf_normalized)
return equ.reshape(img.shape).astype(np.uint8)
这段代码虽然慢,但每一步都看得见。我建议初学者至少手写一次,理解映射过程。
2.3 局限性分析:噪声放大与过增强
全局 HE 最大的问题是什么?我直接说结论:噪声放大 + 过增强。
为什么会这样?因为 HE 是全局操作。它不管某个区域是「细节丰富的暗部」还是「纯噪声的暗部」,一律拉伸。结果就是:噪声被放大,亮部被过度提亮,整张图看起来「假假的」。
我曾经踩过的坑:有一次做夜间监控视频增强,直接用全局 HE。结果画面里的雪花噪点全被放大,人脸反而看不清了。后来改用自适应直方图均衡化(CLAHE),才把问题解决。
具体来说,全局 HE 有三大硬伤:
| 问题 | 表现 | 原因 |
|---|---|---|
| 噪声放大 | 暗部出现明显颗粒感 | HE 把低灰度区的微小差异也拉伸了 |
| 过增强 | 亮部细节丢失,出现「光晕」 | 全局映射导致高灰度区被过度压缩 |
| 对比度不自然 | 图像看起来「塑料感」 | 直方图被强制拉平,破坏了原有分布 |
我举个例子。一张夜景照片,天空部分本来只有一点点噪声。HE 之后,噪声变成了一粒一粒的白点。为什么?因为天空的像素值集中在 10~20,HE 把这个范围拉伸到了 0~80。原本相差 1 的两个像素,现在相差 4。噪声被放大了 4 倍。
另一个常见问题是「过增强」。比如人脸区域,原本亮度适中。HE 为了把暗部拉亮,把整个人脸的对比度也拉高了,结果颧骨、额头出现不自然的亮斑。这在医疗影像里尤其致命——可能会掩盖病灶。
一句话总结:全局 HE 适合「直方图集中且无噪声」的图像。对于低光照片,尤其是手机夜景、监控视频,直接上 HE 大概率翻车。
2.4 知识体系:HE 在低光增强中的位置
下面这张图是我自己整理的,帮你理清 HE 在整个低光增强技术栈里的位置。
你看,HE 的原理不复杂,实现也简单。但真正落地时,局限性才是关键。我个人的经验是:全局 HE 只适合做「快速预览」或「直方图非常集中的简单场景」。真要用于产品,至少得用 CLAHE(自适应直方图均衡化),或者结合去噪预处理。
避坑指南:如果你非要用全局 HE,记得先做一步高斯模糊或中值滤波,把噪声压一压。虽然会损失一点细节,但至少不会出现「噪声爆炸」的情况。
好了,这一章就到这里。HE 是基础,但也是很多高级增强方法的起点。理解了它的原理和局限,后面学 CLAHE、Retinex 这些方法时,你会更容易抓住本质。