5.21. 缩放、翻转和裁剪¶
前面的各小节都是在像素原来的位置上对其进行处理。而变换这一类操作改变了这一点。缩放会把每个输入像素送到一个不同的输出位置,可能一次送到多个输出位置(放大时),也可能送到一个与多个其他输入像素共享的位置(缩小时)。翻转和旋转通过不同的映射做同样的事。裁剪保留输入像素中的一个矩形子集,并丢弃其余部分。
image 模块通过三个方法暴露这一类操作,它们共享大部分参数和大部分行为:
copy()—— 生成图像的副本,可能经过缩放、裁剪或重新定向。crop()—— 与copy相同的操作,但预期应用将从源图像中选取一个子矩形。scale()—— 同样的操作,但预期应用将调整结果的尺寸。
这三者共享相同的参数和相同的变换机制;区别在于结果默认落在何处。copy() 生成一幅新图像,而 crop() 和 scale() 则就地修改源图像。
5.21.2. 插值:AREA、BILINEAR、BICUBIC¶
当缩放将每个输出像素送到一个与任何单个输入像素都不对齐的位置时,该方法必须决定写入什么值。三个标志控制其方式:
image.BILINEAR 在最近的四个输入像素之间进行插值,按它们到输出位置的距离加权。结果比最近邻法更平滑,对角线上没有可见的锯齿,但额外的运算开销约为最近邻遍历的四倍。它是大多数放大工作以及任何非整数缩放因子的恰当选择。
image.BICUBIC 使用三次曲线在最近的十六个输入像素之间进行插值,结果更加平滑,但运算量也随之进一步增加。对于需要的、对成本敏感的应用而言这是最佳质量;但对于 IDE 仅用于显示的实时画面而言,额外的计算很少值得。
image.AREA 对落在输出像素覆盖范围内的每个输入像素取平均——这是缩小的恰当算法。双线性和双三次都是插值器:它们估计源像素之间的值,这正是放大所需要的,但在缩小时每个输出像素覆盖许多源像素,而插值器只读取其中最近的几个——它跳过的细节会以混叠的形式回来。image.AREA 则会把每个被覆盖的像素都折算进平均值。
不带任何 hint 的默认缩放算法是最近邻法,当源图像已经处于目标的像素分辨率时,它最便宜,也是正确答案。
5.21.3. 定向:翻转和旋转¶
定向标志是一小组布尔变换,它们彼此之间以及与插值标志之间都可以自由组合:
image.VFLIP将图像垂直翻转(顶部变为底部)。image.HMIRROR将其水平镜像(左侧变为右侧)。image.TRANSPOSE交换 x 轴和 y 轴(行变为列)。
大多数旋转由组合这三者得到。该模块还暴露了命名的快捷方式:
image.ROTATE_90(=VFLIP | TRANSPOSE)image.ROTATE_180(=HMIRROR | VFLIP)image.ROTATE_270(=HMIRROR | TRANSPOSE)
代码示例:
img.copy(hint=image.ROTATE_90, copy_to_fb=True)
5.21.4. 宽高比处理¶
当源图像的宽高比与要绘制到的矩形不匹配时,三个标志决定如何处理这种不匹配:
image.SCALE_ASPECT_KEEP 保持源图像的宽高比并对结果进行信箱化处理——源图像被缩放到刚好能放入目标内,目标的其余部分用空(零)像素填充。当保持源图像不变形比填满整个输出更重要时,这是恰当的选择。
image.SCALE_ASPECT_EXPAND 保持源图像的宽高比并对其进行裁剪——源图像被缩放到填满目标,超出目标的部分被裁去。当填满整个输出比看到源图像的每一部分更重要时,这是恰当的选择。
image.SCALE_ASPECT_IGNORE 忽略宽高比,将源图像拉伸以填满目标,接受由此引入的任何变形。当应用已经考虑到这种变形时,这是恰当的选择——例如,当目标的尺寸实际上并非同一场景的等比矩形时。
默认(未设置宽高比标志)与 SCALE_ASPECT_IGNORE 相同:拉伸以填满。关心宽高比的应用应明确指定这三者之一。
5.21.5. 何时使用哪一个¶
大多数尺寸调整使用 scale(),配合一对 x_scale / y_scale 和一个插值 hint:
img.scale(x_scale=0.5, y_scale=0.5, hint=image.AREA)
大多数旋转使用同样的调用,配合 hint=image.ROTATE_90 或类似参数。
裁剪使用 crop(),配合一个非默认的 roi:
img.crop(roi=(40, 30, 200, 150))
当源图像必须在操作后保留时——例如捕获参考帧,或为一帧即将被破坏性处理的图像拍摄缩略图——copy() 会将结果生成为一幅新图像,并保持源图像不受影响:
thumbnail = img.copy(x_scale=0.25, y_scale=0.25, hint=image.AREA)
这个默认行为正是三个名称背后的真正区别:scale 和 crop 就地变换,copy 进行分配。结果放置关键字弥合了这一差异:在 scale 或 crop 上使用 copy=True 会将结果分配为一个单独的堆缓冲区,而非覆盖源图像;在这三者中任意一个上使用 copy_to_fb=True 都会将结果放入帧缓冲区以供 IDE 预览。