5.20. 回归与相似度

Image 类上还有另外两种测量方法,它们将图像汇总为像素值分布之外的某种东西。对阈值化像素进行线性回归,可为应用提供一条可据以行动的直线——这是循线机器人的经典输入。相似度测量则为应用提供一个描述两幅图像有多相像的数值——这是黄金图像回归测试或粗略变化检测器的天然输入。

5.20.1. 线性回归

当图像的前景像素恰好在帧内构成一条直线时——机器人正在跟随的赛道上的胶带、地平线、道路或走廊的边缘——应用通常并不需要每一个单独的前景像素。它想要的是穿过所有这些像素的最佳拟合直线,并以参数形式表示,以便判断这条线的朝向以及它在帧内的穿越位置。

get_regression() 执行这一拟合。它接受与 binary()find_blobs() 相同的阈值元组形式,识别出每一个匹配阈值的像素,并返回单个 line 结果,描述穿过这些像素的最佳拟合直线:

line = img.get_regression([(0, 60)])
if line:
    img.draw_line((line.x1(), line.y1(),
                   line.x2(), line.y2()),
                  color=(255, 0, 0))

该拟合采用 Theil-Sen 线性回归——一种比更常见的最小二乘拟合更能容忍离群值的稳健方法。少数远离真实直线的像素不会像在最小二乘法中那样扭曲结果,这与真实阈值输出中前景含噪的实际情况相吻合。

line 结果承载了被裁剪到图像矩形内的端点(x1y1x2y2)、直线的长度和幅值(lengthmagnitude),以及直线的极坐标几何描述(thetarho)——即直线相对水平方向的角度及其到原点的垂直距离。极坐标形式通常正是控制回路所需要的:theta 告诉机器人直线朝哪个方向倾斜,rho 告诉它直线在何处穿过图像,对二者的反馈回路可使机器人保持在直线中心。

若干关键字参数可用于调节稳健性和开销。x_stridey_stride 在拟合时跳过像素——步长越大,回归越便宜,代价是拟合的像素越少。area_thresholdpixels_threshold 会拒绝背后匹配像素不足的直线。target_size 在拟合前将输入重新缩放到更小的尺寸——在图像的 80×60 替代版本上运行回归会更快,而直线方向精度的损失不大。

如果无法拟合出可接受的直线——若阈值未匹配任何像素,或匹配出的模式看起来不像一条直线——该方法返回 None。真实的循线代码会在访问直线属性之前,为每一次 get_regression() 调用加上 None 检查。

5.20.2. 图像相似度

另一种测量:不再问“图像中包含什么?”,而是问“这两幅图像有多相像?”。可以采用的操作是 get_similarity(),它计算源图像与参考图像之间的结构相似性指数(SSIM)。

s = img.get_similarity(reference)
print(s.mean, s.stdev)

SSIM 是图像处理领域通用的标准图像相似度度量,因为它的行为方式符合人类对相似性的直觉——微小的位移或微小的亮度变化只会略微降低分数,而大的结构性变化(物体缺失、场景不同)则会显著降低分数。分数范围从 -1+1+1 表示两幅图像完全相同,0 表示二者不相关,-1 表示二者在结构上相反。返回的 similarity 对象提供整幅图像的平均 SSIM,以及各图块分数的标准差、最小值和最大值。

对于那种数值优于大数值的比较——例如一个在“无变化”时应报告零、并随变化累积而上升的回归测试——dssim=True 标志会返回结构不相似度:即用 1 减去平均 SSIM,因此对于完全相同的图像返回值为 0.0,并随二者差异增大而上升。

5.20.3. SSIM 的应用场景

两种常见应用:

黄金图像回归测试。测试框架在已知良好的条件下捕获一帧参考帧,并将其存储为黄金图像。后续的测试运行在相同条件下捕获图像,并用 SSIM 与黄金图像比较。分数高于某个阈值(视容差而定为 0.950.98)即通过;低于该阈值即失败。测试框架无需知道什么发生了变化——SSIM 分数本身就是信号。

粗略变化检测。如果应用想要一种比帧差更粗粒度的方式——忽略微小亮度变化但对大的结构性变化作出反应——可以使用针对参考帧的 SSIM,而不是逐像素的 difference() 后再加阈值。SSIM 比逐像素差分对光照漂移更不敏感,因此当目标是检测“场景看起来发生了实质性变化”而非“任何单个像素发生了变化”时,它是更好的选择。

两种应用都使用相同的调用——img.get_similarity(reference)——并根据返回分数的阈值触发。区别仅在于阈值是高(回归测试,寻找近乎相同的匹配)还是低(变化检测,寻找任何大的结构性变化)。

5.20.4. 变换并比较的形式

一个实用的微妙之处:get_similarity() 接受与 draw_image() 相同的 xyx_scaley_scaleroirgb_channelalphacolor_palettealpha_palettehinttransform 参数。参考图像会在 SSIM 比较运行之前,按这些参数进行定位、缩放和变换。

这意味着应用可以询问“在经过已知的位移/旋转/缩放之后,这个场景与参考帧有多相似”,而无需准备一幅预先变换好的参考图像。这是一种廉价的方式,可用于构建一个在参数空间中搜索、并报告参考图像的哪种变换最匹配当前帧的跟踪器。