5.7. 合成图像

上一页中的绘图基元在图像上绘制几何标记——一条线、一个矩形、一段文本。这涵盖了算法需要可视化的大多数标注,但并非全部。有时标注本身就是一幅图像:一个用于与当前帧并排显示的参考帧捕获、显示在预览角落里的上一次捕获缩略图、为校准而叠加在实时帧之上的先前存储的模板。将一幅图像绘制到另一幅图像上的机制只有一个方法——draw_image()——它带有足够多的参数,可以处理真正的合成所需的位置、缩放、调色板以及透明度。

5.7.1. 基本调用

其最简单的形式是,draw_image 接受另一个 Image 和一个绘制位置:

reference = image.Image("/sdcard/reference.bmp")
img.draw_image(reference, x=10, y=10)

目标是 img;源是 reference;源的左上角像素落在 img(10, 10) 处,源的其余像素则从该处向右、向下依次排列。被源覆盖的目标像素会被源的对应像素覆写;位于源覆盖范围之外的像素保持不变。

如果源超出了目标的边缘,超出的部分会被静默裁剪——这与 set_pixel 对越界位置所表现出的宽容行为相同。应用代码无需预先将位置限制在图像尺寸范围内;它可以传入想要的位置,让该方法处理裁剪。

5.7.2. 内联加载文件

draw_image 接受文件路径来代替 Image 参数,并在合成之前加载该文件:

img.draw_image("/sdcard/reference.bmp", x=10, y=10)

这看起来像是一种便利——用一行代替两行——确实如此,但其中的区别不止是语法。从文件构造一个 Image 会分配一个缓冲区来保存解码后的像素,并且该缓冲区会一直存在,直到垃圾回收将其释放。将路径直接传给 draw_image 则让该模块把文件解码到一个临时缓冲区中,从中进行合成,并在调用返回时释放该缓冲区,而无需应用代码在帧与帧之间持有对单独 Image 的引用。

5.7.3. 缩放

当源和目标尺寸不同时——比如将一个低分辨率捕获合成到更高分辨率的画布上,或者一个需要按帧的特定比例调整大小的缩略图——两个缩放参数负责在绘制时调整源的大小:

img.draw_image(reference, x=10, y=10, x_scale=2.0, y_scale=2.0)

x_scaley_scale 是相互独立的浮点数;两者传入相同的值会进行均匀缩放,传入不同的值则会沿某一轴拉伸或收缩源。缩放发生在绘制时;源 reference 不会被修改。

提示标志组成的位掩码决定了缩放实际上如何在像素之间进行插值。image.BILINEAR 以更多计算为代价产生更平滑的结果;image.BICUBIC 产生更加平滑的结果,开销也更大;默认使用最近邻插值,它是最廉价的,并且在源已经处于目标的像素分辨率时是正确的选择。宽高比处理标志——SCALE_ASPECT_KEEPSCALE_ASPECT_EXPANDSCALE_ASPECT_IGNORE——决定当源的宽高比与其被绘入的矩形不匹配时该如何处理。

5.7.4. Alpha 混合

默认情况下,draw_image 会用源像素替换目标像素。当目标是半透明叠加层时——使目标透过源显示出来——alpha 参数控制两者如何混合。alpha=0 只显示目标(不显示源);alpha=255 是默认值,只显示源(完全替换);中间值则按比例混合两者:

img.draw_image(overlay, x=0, y=0, alpha=128)

单独的 alpha_palette 参数是该模块唯一的逐像素 alpha 机制。它接受一幅 GRAYSCALE 图像,其值被用作源中对应位置处的 alpha——例如,一个 alpha 随其强度变化的热力图。alpha 必须以那个单独的灰度参数提供;一幅自带 alpha 通道的源图像(比如带透明度的 PNG)不会自动带入它。

5.7.5. 源 ROI 与调色板

另外两个参数完善了合成机制:

  • roi=(x, y, w, h) 将源限制为其自身的一个子矩形,因此只有该矩形会被合成到目标上。这在同一次调用内进行裁剪时很有用,无需准备一个裁剪后的中间图像。

  • color_palette 在绘制之前通过查找表替换每个源像素的值——这与 to_rainbow()to_ironbow() 使用的机制相同,此处将其暴露出来,使叠加层在进入目标的过程中即可被调色板化,而无需单独的转换步骤。

两者都与该调用上的其他一切协同工作:缩放、alpha、目标侧的 mask 参数,以及将写入范围限定为目标某个矩形的目标侧 roi 参数。