3. 直方图均衡化:让图像对比度“活”起来

直方图均衡化,说白了就是把一张灰蒙蒙的图变得层次分明。我刚开始接触图像处理时,总觉得这玩意儿有点玄乎——不就是拉拉对比度吗?后来在项目中处理过一批低照度监控图像,才真正体会到它的威力。

这一节,咱们就把它彻底讲透。

3.1 直方图均衡化的原理

先问个问题:什么样的图像看起来最舒服?

我的经验是——像素值分布比较均匀的图像。暗部有细节,亮部不过曝,中间调层次丰富。直方图均衡化干的事,就是把原始图像的灰度分布“拉伸”开,让每个灰度级上的像素数量大致相等。

数学上,它用的是累积分布函数(CDF)做映射。公式长这样:

s_k = T(r_k) = (L-1) * Σ p_r(r_j)   (j = 0 到 k)

其中:

  • r_k:原始图像的第 k 个灰度级
  • s_k:映射后的灰度级
  • L:灰度级总数(通常是256)
  • p_r(r_j):第 j 个灰度级的概率密度

嗯,公式看着有点唬人。但你可以这么理解:它把直方图的“山峰”削平,填到“山谷”里,让整个分布更平坦。

我的小经验: 均衡化后的图像,直方图并不是平坦的直线,而是近似平坦。因为灰度级是离散的,不可能做到完美均匀分布。别纠结这个,效果到位就行。

3.2 histeq 函数:最基础的均衡化工具

MATLAB 里实现全局直方图均衡化,用 histeq 就对了。语法极其简单:

J = histeq(I);          % 对灰度图 I 做均衡化
J = histeq(I, n);       % 指定输出灰度级数 n(默认64)

来看个完整例子:

% 读取图像
I = imread('pout.tif');

% 直方图均衡化
J = histeq(I);

% 显示对比
subplot(2,2,1); imshow(I); title('原始图像');
subplot(2,2,2); imhist(I); title('原始直方图');
subplot(2,2,3); imshow(J); title('均衡化后');
subplot(2,2,4); imhist(J); title('均衡化直方图');

运行这段代码,你会看到:原始直方图集中在中间区域,均衡化后被拉伸到了整个灰度范围。图像对比度明显提升,细节也出来了。

注意: histeq 对整张图做全局映射。如果图像本身有局部过暗或过亮区域,全局均衡化可能会放大噪声,或者让某些区域失真。我曾经在一个医学影像项目里吃过这个亏——全局均衡化后,病灶区域的对比度反而下降了。

3.3 adapthisteq 函数:局部均衡化,更精细的控制

为了解决全局均衡化的局限性,MATLAB 提供了 adapthisteq——自适应直方图均衡化。它把图像分成一个个小块(tiles),对每个小块单独做均衡化,然后用插值消除块之间的边界效应。

基本用法:

J = adapthisteq(I);
J = adapthisteq(I, Name, Value);

常用参数:

参数名 说明 默认值 我的建议
'NumTiles' 分块数,如 [8 8] 表示分成 8×8 块 [8 8] 块数越多,局部适应性越强,但计算量也越大
'ClipLimit' 对比度限制,值越大对比度增强越强 0.01 我一般从 0.02 开始试,效果不够再往上加
'Distribution' 目标直方图形状:'uniform'、'rayleigh'、'exponential' 'uniform' 大部分场景用 uniform 就行
'Alpha' 当 Distribution 为 'rayleigh' 时的分布参数 0.4 调高会让亮部更亮

实战代码:

% 读取低照度图像
I = imread('lowlight.jpg');

% 全局均衡化
J_global = histeq(I);

% 自适应均衡化(推荐参数)
J_adapt = adapthisteq(I, 'NumTiles', [8 8], 'ClipLimit', 0.02);

% 对比显示
figure;
subplot(1,3,1); imshow(I); title('原始图像');
subplot(1,3,2); imshow(J_global); title('全局均衡化');
subplot(1,3,3); imshow(J_adapt); title('自适应均衡化');
关键区别: histeq 是“一刀切”,adapthisteq 是“因地制宜”。对于光照不均匀的图像(比如中心亮、四周暗),adapthisteq 的效果明显更好。

3.4 均衡化效果对比:什么时候该用哪个?

我整理了一个对比表,方便你快速决策:

场景 推荐方法 原因
整体偏暗/偏亮,光照均匀 histeq 简单快速,效果足够
光照不均匀(如中心亮四周暗) adapthisteq 局部自适应,避免过曝或欠曝
医学影像(X光、CT) adapthisteq 保留局部细节,避免噪声放大
实时视频处理 histeq 计算量小,帧率高
图像中有大量平坦区域(如天空) adapthisteq + 低 ClipLimit 避免平坦区域产生块状伪影

我曾经在一个车牌识别项目里踩过坑:用 histeq 做预处理,结果车牌区域对比度反而下降了。后来换成 adapthisteq,把 NumTiles 设成 [4 4],ClipLimit 调到 0.015,效果立竿见影。嗯,参数调优这事儿,真得靠试。

3.5 核心流程:一张图看懂均衡化

下面这张 SVG 图,把直方图均衡化的完整流程和两种方法的区别都画出来了:

直方图均衡化核心流程 输入原始图像 计算灰度直方图 计算累积分布函数 全局映射 (histeq) 整图使用同一映射函数 局部映射 (adapthisteq) 分块处理 + 插值融合 输出增强图像 选择依据:图像光照均匀度、噪声水平、实时性要求

3.6 避坑指南

最后,分享几个我踩过的坑:

  • 彩色图像别直接均衡化——先把 RGB 转到 HSV 或 Lab 色彩空间,只对亮度通道做均衡化,否则颜色会严重失真。
  • adapthisteq 的 ClipLimit 别设太大——超过 0.1 容易产生“块状效应”,图像看起来像打了马赛克。
  • 噪声图像慎用 histeq——它会同时增强噪声。我一般先做一次中值滤波或高斯滤波,再做均衡化。
  • 别迷信默认参数——每个项目的图像特性不同,花点时间调参,效果能提升一个档次。
调试小技巧: 用 imhist 观察均衡化前后的直方图分布,如果发现某个灰度级出现“断崖式”空白,说明映射函数可能过度拉伸了。这时候可以试试 adapthisteq 并降低 ClipLimit。