1  数字图像基础

在镜头把光线汇聚到传感器靶面的那一瞬间,“图像”还是一个连续的物理量:像平面上每一点都有自己的照度,亮度可以取任意实数值。而工控机里的图像处理程序拿到的,却是一个整数矩阵。从前者到后者,相机内部发生了两次离散化:采样(sampling)——传感器的像元阵列把连续的像平面切成有限个网格点,决定了图像在空间上”看得多细”;量化(quantization)——模数转换器把每个像元上连续的电荷量压成有限级的整数,决定了图像在灰度上”分得多精”。这两步各丢掉了一部分信息。丢掉的部分什么时候无关紧要、什么时候会反过来毁掉整个视觉方案,正是本章要回答的问题。

为了让两种离散化的后果都能被肉眼看见,本章构造了一幅 480×360 的 8bit 灰度测试场景(图 1.1):水平方向从 40 到 160 的线性渐变背景和一个朗伯(Lambert)球面提供平滑连续的灰度——它们对量化最敏感;一组嵌套的阶跃矩形(暗 20 内嵌亮 235)提供陡峭边缘;宽度分别为 1、2、3 像素的竖条组和一条频率自左向右连续升高的啁啾(chirp)条纹带提供细小结构——它们对采样最敏感。后续所有实验图都由 code/digital_images/ 下的程序真实生成。

图 1.1: 基准测试场景(480×360,8bit)。左上:暗矩形内嵌亮矩形(阶跃边缘);右上:朗伯球(连续灰度曲面);背景为 40→160 的水平渐变;左下:宽 1/2/3 px 的竖条组;底部:频率向右递增的 chirp 条纹带。

1.1 图像作为二维信号

把镜头投在传感器上的连续像记作 \(f_c(x, y)\)。采样把它变成只在整数网格上有定义的离散图像

\[ f[n, m] = f_c(m \Delta_x,\ n \Delta_y), \]

其中 \(n\) 是行下标(向下增长),\(m\) 是列下标(向右增长),\(\Delta_x, \Delta_y\) 是采样间隔——物理上就是像元的间距。量化再把每个 \(f[n,m]\) 的取值限制为有限级整数。于是数字图像最终就是一个整数矩阵,本章的测试场景即为 360 行 × 480 列。

由此引出三个最基本的指标。分辨率(resolution)是采样网格的规模,即图像的宽高像素数,它决定空间细节的上限;位深(bit depth)是每个像素用多少个二进制位表示,决定灰度级数——8bit 对应 \(2^8 = 256\) 级,是工业相机最常见的输出格式;动态范围(dynamic range)则是传感器能同时记录的最亮与最暗信号之比,位深决定了这个范围被切成多少级来表达。三者相互独立:高分辨率不等于高位深,位深高也不代表传感器真能分辨那么多有效灰度级。

工业相机常见位深为 8/10/12bit。多数定位、检测任务用 8bit 足够;但当算法要把灰度当作物理量使用时——例如光度立体(章节 34)从明暗恢复表面法向、背光源亮度均匀性测量——8bit 的 256 级往往不够,需要 10bit 甚至 12bit 输出。

1.2 量化

把 256 级灰度重新量化到 \(L\) 级,规则很简单:以步长 \(\Delta = 256 / L\) 把灰度轴切成 \(L\) 段,像素值落在第 \(q\) 段就记作级别 \(q\)

\[ q[n, m] = \left\lfloor \frac{f[n, m]}{\Delta} \right\rfloor, \qquad \Delta = \frac{256}{L}. \]

量化引入的误差至多半个步长,即 \(\pm\Delta/2\),且在步长内近似均匀分布——可以把它看成一种叠加在图像上的均匀噪声,与 章节 6 讨论的传感器噪声同属”灰度被扰动”的范畴,只是它由我们自己的数字化决定、不可通过平均消除。\(L\) 越小步长越大,这种”噪声”越强。图 1.2 展示了同一场景量化到 16 级(4bit,步长 16)、4 级(2bit,步长 64)和 2 级(1bit,步长 128)的结果。

(a) 16 级(4bit)
(b) 4 级(2bit)
(c) 2 级(1bit)
图 1.2: 灰度量化实验。(a) 16 级:渐变背景出现竖向条带,球面出现同心环,但矩形边缘与细条组几乎不受影响;(b) 4 级:强烈海报化,图像只剩四档灰度;(c) 2 级:非黑即白,球面与右侧亮背景融为一体,球的轮廓只剩一圈细线。

最先暴露量化的不是边缘,而是平滑渐变区。在 图 1.2 (a) 中,背景渐变上出现了一道道清晰的竖向带状伪影(banding),球面则变成一圈圈同心环。原因不难想清楚:渐变背景从 40 爬升到 160,跨越 480 列,相邻像素的灰度差远小于 16 的量化步长,于是连续的坡道被切成一截截等灰度的平台,平台交界处发生整整一个步长的突跳——人眼对大面积的灰度台阶极其敏感,banding 因此一目了然。球面是二维版的同一现象,等灰度平台呈环形,便成了同心环。继续压到 4 级(图 1.2 (b)),整幅图像只剩四档灰度,呈现典型的海报化(posterization)。到 2 级(图 1.2 (c))时只剩一道阈值:球面的大部分和右侧较亮的背景都被归入”白”,两者融成一片,球作为物体的形状信息基本丢失。

反过来看矩形和细条组:直到 4 级量化它们依然轮廓分明。阶跃边缘两侧的灰度差(20 对 235)远大于步长,量化怎么切都切不掉这个反差;细亮条(245)与背景之间同理。边缘和细结构对量化不敏感,平滑渐变区最敏感——这条结论直接对应工程上的位深选型:以找边、定位、有无判断为主的检测类任务,关心的是大反差结构,8bit 几乎总是够用;而要在平缓的灰度变化中读出信息的测量类、光度类任务,才真正需要为更高位深买单。

1.3 采样与混叠初步

量化丢的是灰度,采样丢的是空间细节。直觉的判据是:要分辨一个周期性结构,每个周期至少要落下 2 个采样点——一明一暗各占一个,结构才不至于在网格间隙里溜走。这是采样定理的直觉版,严格的频域表述留到 章节 11。当结构比这个极限更细时,它不会简单地消失,而是会伪装成不存在的粗结构——这种现象叫混叠(aliasing)

实验对场景做空间欠采样:每 4 个(或 8 个)像素取 1 个,不做任何预滤波,再用像素复制放大回原尺寸以便对比,等效分辨率分别为 120×90 和 60×45。结果见 图 1.3

(a) 1/4 采样(等效 120×90)
(b) 1/8 采样(等效 60×45)
图 1.3: 空间欠采样实验(无预滤波,像素复制放大回原尺寸)。(a) 1/4 采样:1 px 与 2 px 条组或消失或并成粗条,chirp 带右侧的密条纹翻折成稀疏的假粗条纹(混叠);(b) 1/8 采样:整图严重像素化,条组退化为零星色块,球面边缘呈明显锯齿。

图 1.3 (a) 中,两类细结构的命运截然不同却同样糟糕。宽 1 px 的条组周期只有 2 px,远低于 4 px 的采样间隔所能分辨的极限:采样点落在条上它就”存在”,落在缝里它就消失,结果原本整齐的六根细条或凭空蒸发、或并成一根粗条——读者若拿它当分辨率板用,会得出完全错误的判断。更具欺骗性的是 chirp 条纹带:它的真实频率向右单调升高,但在欠采样图里,右侧本应最密的条纹反而变成了一组缓慢起伏的条纹。这组”假低频”就是混叠的标准面孔——超过采样极限的高频结构折返回来,冒充低频。危险之处在于结果看起来毫无异常:图像没有变糊,只是内容是错的,且事后无法从欠采样数据中恢复。图 1.3 (b) 的 1/8 采样则把破坏推到极致:条组几乎全军覆没,整图呈强烈的马赛克状像素化,连球面轮廓都成了锯齿。

工业镜头与相机选型中”像素分辨率至少达到最小缺陷尺寸的一半”(即最小缺陷 ≥ 2 个像素)这条经验法则,正源于”每周期 2 个采样”的判据:缺陷至少覆盖两个像素,才能保证无论它落在网格的什么位置都留下可检出的痕迹。实践中为留出信噪比余量,常进一步放宽到 3~5 个像素。

值得强调的是本实验故意不做预滤波。真实相机的镜头模糊和像元的面积积分相当于一层天然的低通预滤波,会先削弱超过采样极限的细节、再让像元采样,混叠因此被抑制了一部分;而对已有数字图像做”隔点抽取”式的缩小则完全没有这层保护——这正是工程中”缩图前要先平滑”的原因,系统的处理方法见 章节 11

1.4 彩色图像与通道

彩色图像在结构上没有新东西:一幅 RGB 图就是三幅同尺寸的灰度图——红、绿、蓝三个通道(channel)——的叠放。图 1.4 的合成场景由三个通道独立绘制:蓝通道是自上而下 60→220 的垂直渐变背景,绿通道是一块亮度 200 的矩形,红通道是一个亮度 220 的圆盘。圆盘与矩形的重叠区红、绿同亮,按加色混合呈黄色;把三个通道分别取出来看,每一个都是普通的灰度图。

(a) 彩色合成场景
(b) R 通道
(c) G 通道
(d) B 通道
图 1.4: 彩色图像与通道分解。(a) 蓝色渐变背景上的红圆盘与绿矩形,重叠区呈黄色;(b) R 通道只有圆盘;(c) G 通道只有矩形;(d) B 通道是渐变背景,圆盘与矩形所占区域均为 0(黑色缺口)。

三个通道在内存里如何排布,有两种方案。交错存储(interleaved)把同一像素的三个分量连续放在一起,逐像素排列,一行的字节数是宽度的 3 倍;平面存储(planar)则把每个通道各自存成一整块连续的灰度图,三块依次排列。交错格式取”一个像素的完整颜色”方便,是相机输出和多数图像库的默认;平面格式则便于对单个通道做整体运算。

交错存储还有一个必须当心的细节:三个分量的先后顺序。本书使用的 SciVision SDK 中,3 通道 SciImage 的交错顺序是 BGR(第 0 字节是蓝),与 OpenCV 的默认约定一致而与”RGB”的字面顺序相反。这一点 SDK 头文件中并无注明,是我们用探针实验实测确认的——本章彩色场景第一次生成时圆盘呈蓝色、背景呈红色,正是通道序假设错误的典型症状。通道序弄反不会报错、不会崩溃,图像看起来也”正常”,只是红蓝互换,对依赖颜色判断的分选算法是致命的。

探针验证法:构造一幅只有单个通道非零的图像(如只写第 0 字节),存盘后用通道序已知的工具(如 OpenCV 或任意看图软件)打开,看显示的是什么颜色,即可确定未注明文档的 SDK 用的是 RGB 还是 BGR。一次实验,胜过任何猜测。

最后是”用不用彩色”的选择。颜色携带的信息对人眼直观,但对算法而言意味着 3 倍的数据量和带宽。大多数工业任务——找边、定位、尺寸测量、缺陷检测——依赖的是亮度结构而非色相,灰度相机配合恰当的打光通常就是最优解;彩色真正不可替代的场合,是目标之间只有颜色差异的任务,如色环电阻识别、彩色线缆分选、印刷品色差检验。即便用了彩色相机,许多算法的第一步也是挑出一个对比度最好的通道当灰度图用。

1.5 图像数据结构与 SciVision

本章实验中所有图像都通过 SciImage 在内存缓冲区上构造。灰度图与彩色图的构造语句分别如下(摘自 code/digital_images/main.cpp):

SciImage img(copy.data(), W, H, W, 1, SCI_IMAGE_8U);        // 灰度:step = W
SciImage color(inter.data(), W * 3, H, W, 3, SCI_IMAGE_8U); // 彩色:step = W*3

构造函数的完整签名是 SciImage(unsigned char* data, int step, int rows, int cols, int channel, SciImageType depth),六个参数逐一解释:

  • data:像素缓冲区首地址。SciImage 不复制数据,缓冲区的生命周期必须覆盖图像对象的使用期。
  • step每行的字节数(行距)。它不是”每行像素数”——对 1 通道 8bit 图它恰好等于宽度 W,对 3 通道图则是 W * 3;若缓冲区每行末尾有对齐填充字节,step 还要把填充算进去。图像库间传图出错,十有八九错在 step。
  • rowscols:图像的行数(高 H)与列数(宽 W)。注意构造顺序是先 step、再行、再列。
  • channel:通道数,灰度为 1,彩色为 3;3 通道时按 BGR 交错排列(上一节的实测结论),写入代码为:
for (int i = 0; i < W * H; ++i) {       // BGR 交错(SDK 实测顺序)
    inter[i * 3 + 0] = cb[i];
    inter[i * 3 + 1] = cg[i];
    inter[i * 3 + 2] = cr[i];
}
  • depth:像素类型枚举(定义于 SciDataDef.h),SCI_IMAGE_8U = 0 表示每通道 8 位无符号整数,即本章一直讨论的 8bit 图像;更高位深的数据对应同一枚举的其他取值。

SciImage 配套的另一个核心结构是 SciROI——感兴趣区域(region of interest, ROI)。产线图像往往只有一小块真正需要处理,把算法限制在 ROI 内既省时间又避开无关干扰,本书后续几乎每一章的算法接口都带 ROI 参数;如何按工件的实际位置动态生成 ROI(位置补正),在 章节 19 中详述。

生成本章全部实验图像的完整可运行工程位于 code/digital_images/,读者可修改量化级数与采样间隔自行复现。

工业案例:8bit 还是 12bit?

某背光面板亮度均匀性测量项目,最初选用 8bit 相机。面板暗角区域的灰度全部挤在十几个灰度级里,相邻一级的跳变就吃掉了百分之几的测量量程,亮度计算的分辨率远达不到验收要求,重复测量的数据无法收敛。换用 12bit 相机后,同样的暗区拥有 16 倍的灰度级数,测量分辨率立刻达标,问题就此解决。但代价同样真实:12bit 像素要占 1.5 倍的存储与传输带宽,相机帧率随之下降,整条流水线的图像缓存都要重新核算。事后复盘的结论是:检测类任务盲目上高位深纯属浪费——位深应当按任务对灰度分辨率的需求来选,而不是”越高越好”。

1.6 小结

  • 数字图像 = 采样 + 量化:采样决定空间分辨率,量化决定灰度级数(位深),两者独立地丢失信息,且丢失后都不可恢复。
  • 量化最先伤害平滑渐变区(banding、同心环),而对阶跃边缘与高反差细结构几乎无碍——检测类任务 8bit 通常够用,把灰度当物理量用的测量类、光度类任务才需要 10/12bit。
  • 细结构需要每周期至少 2 个采样点;欠采样时超限的高频不会消失,而是混叠成以假乱真的低频结构,且事后无法修复——缩图前先平滑,选型时保证最小缺陷覆盖至少 2 个像素。
  • 彩色图是三幅灰度图,交错存储时务必核实通道序:SciVision 的 3 通道 SciImage 实测为 BGR,头文件未注明——对接任何 SDK,一次探针实验胜过猜测。
  • SciImagestep 是每行字节数而非像素数,多通道与行对齐都会让它偏离宽度值,是跨库传图最常见的出错点。

采样、量化与彩色空间的系统论述见 (Gonzalez 和 Woods 2018),其中对色度与各类颜色模型的处理尤为完整;图像形成与数字化过程的现代视角可参阅 (Szeliski 2022)。采样定理”每周期至少 2 个采样点”的严格出处是 Shannon 的奠基性论文 (Shannon 1949)。关于图像采集、传感器特性与工业相机数字化链路更贴近工程的论述,可进一步阅读 Steger 等人的著作 (Steger, Ulrich, 和 Wiedemann 2018)