2、图像直方图基础:灰度直方图的定义、直方图的计算方法、直方图的数学表示

好,我们正式开始讲直方图。

说实话,直方图这个概念,在嵌入式视觉里太基础了。基础到什么程度?我面试新人时,第一个问题往往就是:「你给我说说,什么是灰度直方图?」

别小看这个问题。能讲清楚的人,通常对图像理解不会差。

2.1 灰度直方图的定义

灰度直方图,说白了就是一张统计图。

它统计的是:图像中每个灰度级出现了多少次

举个例子。一张8位深度的灰度图,灰度范围是0到255。直方图就是告诉你:

  • 灰度值为0的像素有多少个
  • 灰度值为1的像素有多少个
  • ……一直到灰度值为255的像素有多少个

嗯,就这么简单。

但简单归简单,它的意义非常大。我在项目中调试摄像头时,第一件事就是看直方图。为什么?因为直方图能直观反映图像的亮度分布对比度曝光是否正常

核心理解:直方图是图像全局信息的「快照」。它不关心像素的位置,只关心像素的「值」出现了多少次。

2.2 直方图的计算方法

计算直方图,其实就是一个计数过程。

你想想看,如果让你手动算一张小图的直方图,你会怎么做?

遍历每个像素,看它的灰度值是多少,然后在对应的计数器上加1。对吧?

计算机也是这么干的。只不过它比你快得多。

我习惯用C语言来实现这个逻辑,因为嵌入式环境里,C是最常见的。下面是一个标准的直方图计算代码:

// 假设图像是8位灰度图,宽width,高height
// hist数组大小为256,初始化为0

void calc_histogram(unsigned char *img, int width, int height, unsigned int *hist) {
    int i;
    int total_pixels = width * height;

    // 清零
    for (i = 0; i < 256; i++) {
        hist[i] = 0;
    }

    // 遍历每个像素,统计灰度值出现次数
    for (i = 0; i < total_pixels; i++) {
        hist[ img[i] ]++;
    }
}

你看,核心就两件事:清零,然后遍历计数。

这里有个细节我要提醒你。我在做嵌入式优化时,发现内存访问模式会影响速度。上面的代码是按顺序访问内存的,缓存友好,效率高。如果你改成随机访问,性能会差很多。

小技巧:在嵌入式平台上,如果图像很大,可以考虑用uint32_t来存hist数组。因为一张1080p的图像有约200万像素,用unsigned int(通常是32位)不会溢出。但如果你用unsigned short(16位),最大只能计65535,会溢出。

2.3 直方图的数学表示

数学上,直方图可以写成这样:

h(r_k) = n_k

其中:

  • r_k 表示第k个灰度级(比如0, 1, 2, ..., 255)
  • n_k 表示灰度级为 r_k 的像素个数

有时候我们也会用归一化直方图,也就是把频数转换成概率:

p(r_k) = n_k / N

这里 N 是图像的总像素数。归一化后,所有 p(r_k) 加起来等于1。

为什么要归一化?

因为不同尺寸的图像,直方图的数值范围不一样。归一化后,你就可以比较不同图像的直方图了。我在做图像质量评估时,经常用归一化直方图来对比两张图的亮度分布是否一致。

另外,直方图还可以用累积分布函数(CDF)来表示:

cdf(r_k) = sum_{i=0}^{k} p(r_i)

这个CDF在后面的直方图均衡化中非常关键。嗯,这里先埋个伏笔。

2.4 直方图的直观理解

光讲数学可能有点枯燥。我来说说怎么看直方图。

假设你拿到一张图的直方图:

  • 直方图集中在左侧(低灰度值):图像偏暗,可能曝光不足
  • 直方图集中在右侧(高灰度值):图像偏亮,可能过曝
  • 直方图集中在中间,两边很少:图像对比度低,看起来灰蒙蒙的
  • 直方图分布均匀,覆盖整个范围:图像对比度高,细节丰富

我曾经在调试一款安防摄像头时,发现夜间图像总是偏暗。一开始以为是传感器问题,后来一看直方图,发现像素全挤在0-30之间。嗯,问题找到了——是增益没调好。调整之后,直方图分布到了0-150,图像明显亮起来了。

注意:直方图只能反映灰度值的分布,不能反映像素的空间位置。两张完全不同的图像,可能拥有完全相同的直方图。所以直方图是「全局特征」,不是「局部特征」。

2.5 直方图在嵌入式视觉中的意义

在嵌入式平台上,直方图计算非常快。为什么?

因为它的计算量很小。一张VGA图像(640x480),总共30万个像素。遍历一遍,做30万次加法,对现在的MCU来说,几毫秒就搞定了。

而且直方图计算可以硬件加速。很多嵌入式视觉芯片(比如TI的DM系列、NXP的i.MX系列)都内置了直方图统计的硬件模块。你只需要配置一下寄存器,硬件自动算好,你直接读结果就行。

我个人建议:在嵌入式项目中,能用硬件直方图就用硬件。省CPU,省功耗,还快。

好,这一节就到这里。直方图是基础中的基础,但也是后续所有直方图相关算法的基石。下一节我们会讲直方图均衡化,到时候你会看到,今天学的这些数学表示,全都会用上。

本节要点回顾:

  • 灰度直方图统计每个灰度级出现的像素个数
  • 计算方法是遍历像素,累加计数
  • 数学表示:h(r_k) = n_k,归一化后为 p(r_k) = n_k / N
  • 累积分布函数CDF是后续均衡化的关键
  • 直方图反映全局亮度分布,不包含空间信息