5.6. 绘制形状和文本¶
一个对图像做出某种判断的算法,往往需要让该判断可见。色块检测器找到了应用所关心的区域;应用希望将该区域绘制到帧上,以便操作员——或运行脚本的开发者——能看到检测到了什么。一个坐标变换把输入位置映射到输出位置;调试它通常意味着在同一幅图像上标出这两个位置。IDE 预览读取它轮询那一刻位于帧缓冲区中的任何内容,因此让算法输出可见的最简单方法,就是把标注写入帧缓冲区本身。Image 类上的绘图系列正是为这项工作准备的工具集。
5.6.1. 绘图基元¶
每个绘图方法在图像上放置一种特定的标记。这个目录很小,并且紧贴标注实际所需的几何基元:
draw_line()—— 两个端点之间的一条直线段。draw_rectangle()—— 一个轴对齐的矩形,空心或填充。draw_circle()—— 围绕中心的一个圆,空心或填充。draw_ellipse()—— 一个可任意旋转的椭圆。draw_cross()—— 在某点处的一个加号,通常用于标记质心或点击目标。draw_arrow()—— 从起点到终点的一个箭头。draw_edges()—— 给定四个角点,绘制任意四边形的四条边;这是勾勒已检测标签或透视扭曲区域轮廓的自然方式。draw_string()—— 使用内置位图字体绘制的文本。
这些方法中的每一个都会就地修改源图像,并返回同一幅图像以便链式调用,遵循先前确立的操作方法惯例。
八个绘图基元,每个面板一个。每个方法绘制一种标记。¶
5.6.2. 颜色¶
每个绘图方法都接受一个 color 参数,它决定向每个被绘制的像素写入什么值。该参数采用的形式取决于图像的格式。对于 RGB565 图像,自然的形式是一个 (r, g, b) 元组,每个通道取值范围为 0 —— 255;该模块会在写入前将其打包成 16 位的 RGB565 字。对于灰度图像,自然的形式是单个整数亮度,从 0(黑)到 255(白)。这些方法也接受该格式的原始存储值——对于 RGB565 是一个 16 位打包字,对于灰度是一个 8 位整数——当颜色已在别处计算好并已处于存储形式时,这是高效的形式。
省略 color 参数会绘制白色。该默认值对灰度处理很方便,其中白色是最大值,并且在大多数背景下都清晰可辨。但对于 RGB565 调试叠加层,它几乎总是错误的:绿色或红色通常在摄像头实际看到的那类场景上更易辨认,而显式的颜色能传达意图。
5.6.3. 粗细与填充¶
大多数几何方法接受两个决定标记如何绘制的标志:
thickness=N设置线宽(以像素为单位)。默认值是1,对大多数叠加层来说足够;当某个标注必须在繁忙的场景上保持可见,或在流水线的后续阶段进一步修改图像之后仍要可见时,较大的值会很有用。fill=True将标记从轮廓切换为实心,用所选颜色绘制每一个内部像素。默认值是False。
这些标志不适用于没有内部可填充的基元——线、十字、箭头、四边形——对于这些基元只有 thickness 有意义。
5.6.4. 绘制文本¶
draw_string() 使用内置的 8×10 像素位图字体绘制字符。x 和 y 是第一个字符单元的左上角,text 是要绘制的字符串,color 遵循与几何方法相同的惯例。该字体涵盖完整的可打印 ASCII 范围且没有字距调整——每个字符占据相同的 8 像素宽单元——这使得输出易于定位。
img.draw_string(10, 10, "blobs: 3", color=(0, 255, 0))
字符串可以包含换行符(\n);每个换行符会把下一个字符移到比上一行低十个像素的新行的开头。scale 参数按一个浮点因子以更大尺寸绘制每个字符,而 x_spacing 和 y_spacing 在每个字符周围添加间距。一小组旋转/镜像/翻转标志可应用于整个字符串或单独应用于每个字符——足以在布局需要时沿某个角度或贴着非水平边缘排布文本。
5.6.5. 清空画布¶
该系列中的一个方法不绘制任何特定标记。它只是把图像的每个像素重置为零:
clear()—— 将每个像素清零,可选地限制在某个 ROI 内或通过掩码限定范围。
当应用每帧都从头合成一个标注时——从黑色画布开始,绘制新的标注,再把结果交给显示——而不是叠加在捕获的帧之上,clear() 就是正确的选择。它也是准备一幅临时图像用作掩码缓冲区的最廉价方式。
一幅新分配的图像在构造时就已经是零,因此 clear() 尤其对在帧间复用的缓冲区才有意义。