4、直方图均衡化算法流程:输入图像、计算直方图、计算累积分布、生成映射表、输出图像

好,咱们今天来聊聊直方图均衡化的具体流程。说实话,这个算法在嵌入式视觉里太常用了。我最早在FPGA上实现它的时候,踩了不少坑,后来才慢慢摸清楚每个步骤的细节。

直方图均衡化,说白了就是让图像的灰度分布更均匀。你想想看,一张图如果整体偏暗或者偏亮,细节就看不清楚。均衡化就是要把这些像素重新分配一下,让亮部和暗部都有足够的细节。

整个流程分五步走:输入图像 → 计算直方图 → 计算累积分布 → 生成映射表 → 输出图像。每一步都有讲究,咱们一个一个说。

4.1 输入图像

这一步看起来简单,其实有门道。我习惯先把图像转成灰度图,因为直方图均衡化本质上是针对单通道的操作。如果是彩色图,你可以对亮度通道单独做,或者对三个通道分别做——但后者容易产生色偏,我个人不太推荐。

我的习惯:在嵌入式平台上,我通常先把RGB转成YUV或YCbCr,只对Y通道做均衡化。这样既能提升对比度,又不会破坏色彩平衡。

输入图像的格式也很重要。8位灰度图,像素值范围0~255,这是最常见的。如果是12位或16位的RAW图,处理逻辑一样,但映射表的大小要相应调整。

4.2 计算直方图

直方图就是统计每个灰度级有多少个像素。比如灰度值0的有100个像素,灰度值1的有50个像素……以此类推。

在嵌入式系统里,这一步通常用硬件加速。我记得在某个项目中,用C语言写了个循环去统计,结果发现CPU占用率飙到80%以上。后来改成用DMA+双缓冲的方式,效率一下就上来了。

// 计算直方图的典型代码(C语言风格)
uint32_t hist[256] = {0};
for (int i = 0; i < width * height; i++) {
    hist[image[i]]++;
}

嗯,这里要注意:直方图数组的大小取决于图像的位深。8位图就是256个bin,10位图就是1024个bin。别搞错了,否则后面计算累积分布时会出问题。

4.3 计算累积分布

累积分布函数(CDF)是直方图均衡化的核心。它表示每个灰度级及之前所有灰度级的像素总数。

公式很简单:CDF(k) = sum(hist[0] + hist[1] + ... + hist[k])

我刚开始做的时候,直接拿浮点数去算,结果在定点DSP上跑得特别慢。后来改成用整数运算,把归一化放到最后一步,速度提升了将近3倍。

// 计算累积分布(整数版本)
uint32_t cdf[256] = {0};
uint32_t sum = 0;
for (int i = 0; i < 256; i++) {
    sum += hist[i];
    cdf[i] = sum;
}
我曾经踩过的坑:如果图像中有大量像素集中在某个灰度级,累积分布会出现陡峭的跳变。这时候直接做映射,会导致图像出现「过曝」或「过暗」的区域。解决办法是加一个裁剪阈值,限制直方图的峰值。

4.4 生成映射表

有了累积分布,接下来就是生成映射表。映射表的作用是把原始灰度值映射到新的灰度值。

标准公式是:new_value = (CDF(old_value) - CDF_min) / (total_pixels - CDF_min) * 255

但在嵌入式平台上,我通常用查表法。提前算好一个256字节的查找表,运行时直接查表,比实时计算快得多。

// 生成映射表
uint8_t lut[256];
uint32_t total = width * height;
uint32_t cdf_min = 0;

// 找到第一个非零的CDF值
for (int i = 0; i < 256; i++) {
    if (cdf[i] != 0) {
        cdf_min = cdf[i];
        break;
    }
}

// 生成LUT
for (int i = 0; i < 256; i++) {
    lut[i] = (uint8_t)((cdf[i] - cdf_min) * 255 / (total - cdf_min));
}

你想想看,这个LUT一旦生成,后面处理每一帧图像就只是查表操作,计算量几乎可以忽略不计。对于实时视频流来说,这个优化非常关键。

4.5 输出图像

最后一步,就是把原始图像的每个像素,通过映射表转换成新的像素值。

// 输出均衡化后的图像
for (int i = 0; i < width * height; i++) {
    output[i] = lut[image[i]];
}

到这里,一张经过直方图均衡化的图像就出来了。你会发现原本灰蒙蒙的图像,现在对比度明显提升,细节也更清晰了。

核心要点总结:

  • 直方图均衡化本质是非线性映射,不是简单的线性拉伸
  • 嵌入式实现时,查表法是最高效的方式
  • 注意处理直方图峰值,避免映射后出现伪影
  • 彩色图像建议只对亮度通道做均衡化

我个人觉得,直方图均衡化是嵌入式视觉里性价比最高的算法之一。实现简单,效果明显,而且对硬件资源要求不高。你在做图像预处理的时候,不妨先试试它,往往能省掉后面很多复杂的处理步骤。

嗯,流程就这些。下一章咱们聊聊直方图均衡化的变种——自适应直方图均衡化(AHE)和限制对比度自适应直方图均衡化(CLAHE)。这两个在实际项目中更常用,尤其是处理光照不均匀的图像时。