5.12. 二值阈值化

许多图像处理流程归根结底都是对每个像素提出的一个问题:这个亮度是否落在表示"前景"的范围内?这个颜色是否足够接近红色,以至于就是应用正在跟踪的标记物?这个像素是否属于流程下一阶段应该关注的候选集合?阈值化就是把这些问题在每个位置上转化为二值答案的运算——像素匹配则置为开,不匹配则置为关——从而把整幅图像简化为一个掩码,供流程的其余部分使用。

5.12.1. binary 方法

binary() 方法用一次调用就能对每个像素执行这种分类。它接受一个阈值范围列表——即像素可以匹配从而算作"开"的条件——并改写图像,使每个至少匹配其中一个范围的像素都被设为该格式的最大值,而每个未匹配的像素都被设为零。其结果就是流程其余部分可以直接使用的二值掩码。

在最简单的形式中,阈值列表只有一个范围,调用返回的是落在该范围内的像素掩码:

img.binary([(120, 255)])

正是列表形式让 binary 变得强大。一个想要跟踪两个彩色标记物的流程,或是想跟踪一个亮度范围加上一个孤立的饱和度峰值的流程,可以把两个范围放在同一个列表中传入,得到一个覆盖所有匹配的单一输出掩码。

顶部是一条横向的灰度渐变,标有 "input -- pixel values from 0 to 255"。在它下方,沿渐变用括号标出了一个从 lo 到 hi 的闭区间阈值范围,圈出了算作匹配的亮度值范围。底部的二值输出条在 lo 到 hi 范围内显示为白色,在范围之外显示为黑色。

阈值化把一幅连续取值的图像转换为一个二值掩码:阈值范围内的每个像素变为该格式的最大值,范围外的每个像素变为零。

5.12.2. 灰度元组

对于灰度图像,阈值列表中的每一项都是一个二元组 (lo, hi),描述一个闭区间亮度范围。值在 lohi 之间(含两端)的像素匹配;落在该范围之外的一切都不匹配。常见的写法很直观:

  • (0, 60) 匹配暗像素——从黑色一直到深灰色。

  • (180, 255) 匹配亮像素——从浅灰色一直到白色。

  • (100, 160) 匹配中灰像素——亮度范围中部的一个区间。

元组内两个值的顺序无关紧要;如果 lo 大于 hi,该方法会在内部将它们交换,因此 (60, 0)(0, 60) 效果相同。

5.12.3. 用于颜色的 LAB 元组

对于 RGB565 图像,每一项都是一个六元组 (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi),它描述的是 LAB 颜色空间中的一个闭区间范围,而不是直接在红、绿、蓝上描述。这些阈值分别是 L(亮度)、A(绿—红色度轴)和 B(蓝—黄色度轴),各自与像素在该通道上的值进行比较。

采用 LAB 而非直接对 RGB 进行阈值化的原因,在于 LAB 颜色空间设计时所围绕的特性:LAB 将亮度与色度分离。两个显示相同颜色但亮度不同的像素,最终会落在不同的 L 值上,但 A 和 B 值大致相同。这种分离使得阈值范围可以通过颜色在 A 轴和 B 轴上的位置来描述一种颜色,并把 L 范围放得很宽,从而在从阴影到高光的每种亮度下都接受该颜色。基于 RGB 的阈值做不到这一点——光照的任何变化都会同时改变 R、G、B 三个值,建立在 RGB 阈值之上的跟踪器在第一次有云遮住太阳时就会失效。

实用的做法是:选取描述应用正在跟踪的颜色的 A 和 B 范围,并把 L 范围放宽——通常是 (0, 100) 以接受任意亮度——除非应用确实想在颜色之外也对亮度做阈值化。

对于少于六个值的元组,缺失的分量默认取最大范围(即该轴上无约束)。因此,RGB565 阈值列表中的一个二元组 (l_lo, l_hi) 只对亮度做阈值化,并匹配所有颜色。

备注

一个真正放得很宽的 L 范围在低端有一个陷阱。随着亮度趋向于零,每种颜色都会收敛到黑色,A 和 B 值塌陷向零并被噪声主导——因此暗像素可能漂移进 A 和 B 范围,从而被当作目标颜色而被跟踪。如果场景中的黑色区域被标记为匹配,请提高 l_lo,直到它们不再被匹配。

5.12.4. 标志

三个关键字参数控制输出:

  • invert=True 翻转结果。每个本应匹配的像素变为零,每个本应为零的像素变为最大值。当用前景不是什么来描述前景更自然时,这很有用。

  • zero=True 改变操作模式:匹配的像素被置零,未匹配的像素保留其原始值。当目标是从图像中擦除匹配的像素,而不是把图像简化为它们的二值掩码时,使用这种方式。

  • to_bitmap=True 将结果作为一幅 BINARY 图像返回,而不是覆盖源图像现有的格式。这种每像素一比特的结果正是后续掩码参数可以直接接受的,而且这种转换往往能省去携带一个完整格式掩码所带来的内存压力。

掩码和 ROI 遵循与本接口其余部分相同的约定:roi 矩形将操作限定在一个子区域内,mask 图像将操作限定在任意位置模式内。

5.12.5. 默认就地操作

与算术运算一样,binary 默认就地运行:源图像的像素会被二值输出覆盖,调用之后原始值便不复存在。当需要保留源图像、且输出应为新分配的 BINARY 图像时,to_bitmap=True 形式是替代方案。此外还接受 copy=True 形式,用于在新缓冲区上得到相同格式的结果。