5.27. 查找圆形和矩形¶
直线和线段覆盖了所捕获帧中的笔直边缘,但摄像头要查找的许多真实世界特征并不是笔直的。放在桌上的硬币是一个圆形。打印的标签、便利贴、以及斜视角下看到的盒子顶面则是一个四边形。image 模块为每一种都提供了专门的检测器:一种用于查找圆形的 Hough 式搜索,以及一种从 AprilTag 衍生而来的四边形搜索。
这两个方法都遵循与直线检测器相同的模式——threshold 控制一个检测结果需要多少票数,roi 缩小搜索范围,返回的对象同时携带位置和置信度大小——但它们的参数空间和合适的默认值差异足够大,值得分别详细说明。
5.27.1. Hough 圆形¶
find_circles() 运行 Hough 变换的圆形变体。来自 Sobel 预处理的每个边缘像素都会为所有可能经过它的圆形投票;获得足够票数的圆形会被返回。
circles = img.find_circles(threshold=3500,
x_margin=10, y_margin=10, r_margin=10,
r_min=10, r_max=80, r_step=2)
for c in circles:
img.draw_circle((c.x, c.y, c.r), color=(255, 0, 0))
threshold 是候选圆形上 Sobel 边缘幅度之和的最小值。较大的圆形描绘出更多像素,因此需要更高的阈值才能通过;一个能找到半径为 20 像素硬币的值,也会在 100 像素边缘周围的噪声上误触发,而一个为大硬币调好的值则会漏掉小的那个。当已知目标半径范围时,合适的阈值会随周长缩放——大致上 threshold = 50 * 2 * pi * r 给出一个合理的起点,而合适的值则可通过简短的调参过程得出。
r_min、r_max 和 r_step 设置半径搜索。如果没有界限,检测器会搜索从几个像素到图像半宽之间的每一个半径,这既慢又容易产生误检。将 r_min 和 r_max 设为以宽裕余量框定预期目标尺寸(例如对于已知约 20 像素的硬币用 r_min=15, r_max=25),可以大幅减少工作量并改善投票的信噪比。r_step 控制搜索的粒度;步长越大运行越快,但可能漏掉真实半径落在两个采样值之间的圆形。默认的 r_step=2 是一个合理的折中。
x_margin、y_margin 和 r_margin 控制邻近检测结果的合并,其方式与 theta_margin 和 rho_margin 对直线检测所起的作用相同。图像中单个物理圆形会为一簇候选圆形投票,这些候选圆形的圆心和半径在几个像素范围内一致;这些 margin 会在构建结果列表之前将每一簇合并到其峰值。较大的 margin 返回更少、更可信的检测结果;较小的 margin 返回更多检测结果,但可能存在近似重复项。
x_stride 和 y_stride 控制投票扫描的步进,方式与其他检测器中相同。默认值 2 和 1 对大多数图像来说没问题;对于已知包含大圆形的图像,把两者都调到 4 是标准的提速折中做法。
每个返回的 Circle 携带 x、y、r(圆心和半径)以及 magnitude(票数总和,可作为用于排序或过滤的置信度分数)。将检测结果绘制回帧中只需一次调用——draw_circle() 接受 (x, y, r) 三元组,可直接从结果中以 (c.x, c.y, c.r) 形式获取。
5.27.2. 矩形¶
find_rects() 借用了 AprilTag 流水线中的四边形检测器——同一段定位标签周围黑色方框的例程被单独暴露出来,作为通用的矩形查找器。
rects = img.find_rects(threshold=12000)
for r in rects:
img.draw_rectangle(r.rect, color=(0, 255, 0))
for corner in r.corners:
img.draw_circle((corner[0], corner[1], 4),
color=(0, 255, 0))
threshold 是矩形周边边缘幅度之和的最小值。在光照良好的帧中,一个打印的黑底白色矩形轻松就能超过 10000;而纹理背景上一个微弱的矩形可能需要降到 2000——以更多误检换取灵敏度。和圆形检测器一样,合适的值也是通过让目标对象出现在视野中、做一次快速调参得出的。
该检测器是投影式的——它查找各边笔直但不一定平行或与坐标轴对齐的四边形。斜视角下看到的标签在图像中看起来像一个梯形,矩形检测器能正确地找到它;与坐标轴对齐的矩形只是其退化情形,即四个角恰好构成一个直角框。roi 限制搜索范围;其余关键字参数沿用 AprilTag 流水线的默认值,很少需要调整。
每个返回的 Rect 携带与坐标轴对齐的边界框——x、y、w、h,外加 draw_rectangle() 所期望的 rect 四元组——以及检测到的四个角,存放在 corners 中。边界框是应用用于粗略定位和尺寸的部分;而这些角描述的是投影四边形本身。当摄像头从某个角度观察一个平面目标,且应用需要消除梯形畸变时——读取标签上的文字、从平面色块采样颜色——这些角可以连同 corners= 关键字直接输入到 rotation_corr() 中(参见 镜头与透视校正),输出便是校正后的矩形,可供接下来的任何分析使用。
警告
由于该检测器是针对 AprilTag 流水线的需求调优的——即边框强烈、高对比度的四边形,例如白纸上的黑色标签轮廓——它并不是一个查找所有矩形的过程。对比度柔和、边缘有纹理或周围杂乱的矩形可能完全检测不到。它的效果如何取决于具体情形:尽早用真实目标测试它,再围绕它构建流水线。
5.27.3. 当检测器误触发时¶
圆形尤其能从对输入做预滤波中获益。嘈杂的帧会产生大量零散的边缘像素,它们全都参与投票,由此得到的 Hough 空间会有宽阔而模糊的峰值,合并器很难将其区分开。在 find_circles() 之前做一次 gaussian() 或 mean() 处理,可以平滑掉噪声并保留真实边缘;检测器会用更短的时间返回更干净的峰值。
对于矩形,常见的失败模式恰恰相反:矩形与其背景之间对比度低,意味着边缘幅度之和始终无法超过 threshold。做一次 histeq() 处理,将亮度范围重新分布到完整的 0 至 255 区间,便能恢复检测器所需的对比度。(对比度必须存在于图像的某处;直方图均衡化只能放大已经存在的东西。)