图像均衡化详解:从直方图均衡到 CLAHE,让图片告别 “灰蒙蒙“
你有没有遇到过这样的图片:要么整体偏暗,细节埋在阴影里;要么偏亮,亮部一片惨白;要么对比度太低,看起来 "灰蒙蒙" 的?比如手机拍的逆光照片,人脸黑得看不清;或者旧相机拍的老照片,明暗糊成一团。这时候,图像均衡化就能帮上忙 —— 它通过调整图片的亮度分布,让暗的地方亮一点、亮的地方暗一点,最终让图片细节更清晰。
一、先搞懂:什么是 "亮度分布"?
要理解均衡化,得先知道图片的 "亮度分布"—— 专业点叫灰度直方图。咱们拿一张灰度图(黑白图)举例:灰度图里每个像素的亮度用 "灰度值" 表示,范围通常是 0(纯黑)~255(纯白)。直方图就是把这些灰度值统计一下:横轴是灰度值(0~255),纵轴是 "有多少个像素是这个灰度值"。
比如:
- 一张偏暗的图,直方图的 "柱子" 主要堆在左边(灰度值低的区域);
- 一张偏亮的图,柱子主要堆在右边(灰度值高的区域);
- 一张对比度低的图,柱子挤在中间一小段(灰度值集中,明暗差别小);
- 一张对比度正常的图,柱子分布比较均匀(从黑到白都有像素,细节丰富)。
举个具体例子:假设test.png
是一张逆光拍的猫的灰度图,猫的脸在阴影里,直方图上 0~50 的柱子特别高(暗像素多),150~255 的柱子也不低(背景亮),但中间 50~150 的柱子很矮 —— 这就是典型的 "明暗两极分化,中间细节少",看起来脸黑、背景刺眼。
而图像均衡化的核心就是:把集中的灰度值 "拉开",让直方图分布更均匀。这样暗的地方能提亮,亮的地方能压暗,中间细节就显出来了。
二、基础款:灰度直方图均衡化
最常用的均衡化方法是灰度直方图均衡化(Histogram Equalization)。它的逻辑很简单:通过一个 "映射规则",把原来集中的灰度值重新分配到 0~255 的整个范围,让每个灰度区间的像素数量尽量均匀。
直方图均衡化的 "三步法"
不用公式也能懂,咱们拿一个简化的例子说清楚。假设一张图的灰度值只有 0~3(共 4 个等级),原始直方图统计如下:
灰度值 | 像素数量 | 占总像素比例(频率) | 累积比例(前 n 个灰度的比例和) |
---|---|---|---|
0 | 50 | 0.5(50%) | 0.5 |
1 | 30 | 0.3(30%) | 0.8(0.5+0.3) |
2 | 15 | 0.15(15%) | 0.95(0.8+0.15) |
3 | 5 | 0.05(5%) | 1.0(0.95+0.05 |
总像素 100 个,原来的灰度集中在 0(50%)和 1(30%),2 和 3 很少,看起来肯定偏暗。均衡化就是要把这些灰度 "重新分配":
步骤 1:算 "累积比例"
就是把每个灰度值的 "频率" 从左到右加起来(上表第 4 列)。比如灰度值 1 的累积比例是 0.8,意思是 "灰度≤1 的像素占 80%"。
步骤 2:用累积比例映射新灰度
新灰度值 = (最大灰度值 - 1)× 累积比例,再取整。这里最大灰度是 3,所以:
- 原灰度 0:新灰度 = 3×0.5=1.5→取 1;
- 原灰度 1:新灰度 = 3×0.8=2.4→取 2;
- 原灰度 2:新灰度 = 3×0.95=2.85→取 3;
- 原灰度 3:新灰度 = 3×1.0=3→取 3。
步骤 3:更新像素值
把原图中所有灰度 0 的像素改成 1,灰度 1 的改成 2,灰度 2 和 3 的改成 3。新的直方图如下:
新灰度值 | 像素数量 | 说明 |
---|---|---|
1 | 50 | 原来的灰度 0 像素 |
2 | 30 | 原来的灰度 1 像素 |
3 | 20 | 原来的灰度 2(15)+3(5) |
现在像素分布在 1~3,没有集中在 0 了,图片就会亮起来,细节更清楚。
实际效果:从 "灰蒙蒙" 到 "清透"
回到test.png
(逆光猫图):
- 均衡化前:直方图左密右疏,暗部像素挤在一起,猫脸细节看不清;
- 均衡化后:直方图被 "拉宽",从 0~255 都有像素分布,暗部(猫脸)被提亮,亮部(背景)被压暗,既能看清猫的胡须,背景也不刺眼。
但要注意:直方图均衡化是 "全局调整"—— 它不管图片里的局部区域,只看整体灰度分布。这就导致它有个缺点:如果图片里有局部亮 / 暗区域,均衡化可能 "顾此失彼"。
比如一张 "一半暗一半亮" 的图(左边是阴影里的树,右边是阳光下的墙):
- 全局均衡化后,左边暗部提亮了,但右边亮部可能因为 "要给暗部让位置" 而过度变暗,甚至变成全黑;
- 或者如果图片有噪声(比如老照片的斑点),均衡化会把噪声也一起 "放大",斑点变得更明显。
三、进阶款:自适应直方图均衡化(CLAHE)
为了解决 "全局均衡化顾此失彼" 的问题,有人提出了自适应直方图均衡化(AHE)—— 简单说就是 "分块处理":把图片分成很多小方块(比如 8x8),每个方块单独做直方图均衡化。这样局部暗的方块单独提亮,局部亮的方块单独压暗,不会互相干扰。
但 AHE 也有问题:如果某个小方块里全是暗像素(比如纯黑的区域),均衡化后会把噪声放大成 "雪花点"。所以实际中更常用的是限制对比度的自适应直方图均衡化(CLAHE)—— 它在 AHE 的基础上加了 "对比度限制":如果某个灰度的像素太多(比如超过一个阈值),就把多余的像素 "分散" 到其他灰度区间,避免局部过度增强噪声。
CLAHE vs 普通均衡化:差距很明显
用一张有 "局部阴影" 的图(比如test.png
是带树荫的路面,有的地方被树挡住暗,有的地方亮):
- 普通均衡化:整体提亮后,亮的路面可能过曝(变成纯白),暗的树荫里细节还是看不清;
- CLAHE:树荫块单独均衡(提亮),路面块单独均衡(压暗),既能看清树荫下的石子,路面也不刺眼,噪声也少。
四、MATLAB 脚本:对比 3 种效果(原图 + 均衡化 + CLAHE)
下面是完整的 MATLAB 脚本,读入test.png
,分别做 "原图直方图、普通直方图均衡化、CLAHE" 的效果对比,帮你直观看到差异。记得把test.png
和脚本放同一文件夹哦!
% 图像均衡化效果对比(原图+直方图均衡+CLAHE)
clear; clc; close all;% 1. 读入图片并转灰度图
img = imread('test.png');
if size(img, 3) == 3 % 若为彩色图,转灰度图img_gray = rgb2gray(img);
elseimg_gray = img; % 已为灰度图则直接使用
end% 2. 计算原图的灰度直方图
[counts_original, gray_levels] = imhist(img_gray);% 3. 普通直方图均衡化(全局均衡)
img_eq = histeq(img_gray); % MATLAB自带histeq函数实现
[counts_eq, ~] = imhist(img_eq); % 均衡化后的直方图% 4. CLAHE(限制对比度自适应均衡化)
% 修正参数名:'NumTiles'控制分块大小([行数, 列数]),默认[8,8]
% 'ClipLimit'控制对比度限制(默认0.01,值越大增强越明显但可能放大噪声)
img_clahe = adapthisteq(img_gray, 'ClipLimit', 0.02, 'NumTiles', [16 16]);
[counts_clahe, ~] = imhist(img_clahe); % CLAHE后的直方图% 5. 对比显示结果(6个子图:原图/均衡图/CLAHE图 + 对应直方图)
figure('Position', [100, 100, 1200, 800]); % 设置图窗大小% 5.1 原图与原图直方图
subplot(3, 2, 1);
imshow(img_gray);
title('1. 原始灰度图');subplot(3, 2, 2);
bar(gray_levels, counts_original, 'FaceColor', [0.8 0.8 1]); % 浅蓝色柱子
xlabel('灰度值(0~255)'); ylabel('像素数量');
title('原始灰度直方图');
xlim([0, 255]); % 固定x轴范围,便于对比% 5.2 普通均衡化图与直方图
subplot(3, 2, 3);
imshow(img_eq);
title('2. 普通直方图均衡化');subplot(3, 2, 4);
bar(gray_levels, counts_eq, 'FaceColor', [0.8 1 0.8]); % 浅绿色柱子
xlabel('灰度值(0~255)'); ylabel('像素数量');
title('均衡化后直方图');
xlim([0, 255]);% 5.3 CLAHE图与直方图
subplot(3, 2, 5);
imshow(img_clahe);
title('3. CLAHE(自适应均衡化)');subplot(3, 2, 6);
bar(gray_levels, counts_clahe, 'FaceColor', [1 0.8 0.8]); % 浅红色柱子
xlabel('灰度值(0~255)'); ylabel('像素数量');
title('CLAHE后直方图');
xlim([0, 255]);% 保存结果图到当前文件夹
saveas(gcf, '均衡化效果对比.png');
disp('处理完成!结果图已保存为"均衡化效果对比.png"');
五、效果说明:3 种图对比看差异
运行脚本后,会生成一张 6 图对比的结果(均衡化效果对比.png
),重点看这几点:
原图 vs 普通均衡化:
- 原图直方图如果集中在左 / 中 / 右(偏暗 / 灰 / 亮),均衡化后的直方图会 "拉宽",覆盖 0~255 更多区域;
- 图片整体对比度提升,但如果原图有局部亮暗差异(比如逆光),可能出现 "亮部过曝" 或 "暗部噪声放大"。
普通均衡化 vs CLAHE:
- CLAHE 的直方图更 "均匀且柔和",不会像普通均衡化那样有突然的 "高柱子";
- 图片局部细节更清晰(比如暗部的小纹理),亮部不会过曝,噪声也更少 —— 尤其适合有局部阴影、复杂明暗的图(如风景、人像、老照片)。
另外,特别注意,图像增强的目的可以是多种多样的,均衡化是为了让亮度分布更合理,能够展现更多的清晰细节,并不一定是最自然、看起来感官体验最清晰的,因此如果你的test.png本来就像扒谱机一样是一个很好看的小猫,可能均衡化的结果并不是非常的自然。
六、实用技巧:什么时候用哪种均衡化?
普通直方图均衡化:适合 "整体偏暗 / 偏亮、无明显局部阴影" 的图,比如阴天拍的远景(整体灰蒙蒙)、曝光不足的证件照 —— 优点是简单快,缺点是局部细节可能照顾不到。
CLAHE:适合 "有局部阴影、明暗复杂" 的图,比如逆光人像(脸暗背景亮)、树荫下的路面、老照片(有斑驳噪声)—— 优点是局部细节好,缺点是参数需要微调(比如
ClipLimit
:噪声多就设小一点,比如 0.01;需要强增强就设大一点,比如 0.03)。不用均衡化的情况:如果图片本身对比度正常(比如晴天拍的清晰照片),均衡化后反而会 "过度增强",导致色彩失真(比如皮肤变成惨白)、细节模糊。
总结:均衡化的核心是 "让亮度分布更合理"
不管是普通直方图均衡化,还是进阶的 CLAHE,本质都是通过调整灰度分布,让图片的 "亮度资源"(0~255 的灰度值)用得更合理 —— 暗的地方别挤在一块,亮的地方别占满空间,最终让眼睛能看清更多细节。
实际用的时候不用纠结原理,先试普通均衡化,效果不好就换 CLAHE,调调ClipLimit
参数(MATLAB 的adapthisteq
函数默认参数就不错,新手可以直接用)。一张 "灰蒙蒙" 的图,可能只需一行代码,就能变得清透 —— 这就是均衡化的魅力~