旋转边界框上的变换¶
本示例说明如何定义和使用旋转边界框。
注意
TorchVision 0.23 中发布了对旋转边界框的支持,目前仍处于 BETA 阶段。我们预计 API 不会更改,但可能存在一些罕见的边缘情况。如果您发现任何问题,请在我们的 bug 跟踪器上报告:https://github.com/pytorch/vision/issues?q=is:open+is:issue
首先,一些设置代码
from PIL import Image
from pathlib import Path
import matplotlib.pyplot as plt
import torch
from torchvision.tv_tensors import BoundingBoxes
from torchvision.transforms import v2
from helpers import plot
plt.rcParams["figure.figsize"] = [10, 5]
plt.rcParams["savefig.bbox"] = "tight"
# if you change the seed, make sure that the randomly-applied transforms
# properly show that the image can be both transformed and *not* transformed!
torch.manual_seed(0)
# If you're trying to run that on Colab, you can download the assets and the
# helpers from https://github.com/pytorch/vision/tree/main/gallery/
orig_img = Image.open(Path('../assets') / 'leaning_tower.jpg')
创建旋转边界框¶
通过实例化 BoundingBoxes
类来创建旋转边界框。构造函数的 format
参数决定边界框是否为旋转的。在此实例中,我们使用 CXCYWHR BoundingBoxFormat
。前两个值是边界框中心的 X 和 Y 坐标。接下来的两个值是边界框的宽度和高度,最后一个值是边界框的旋转角度(以度为单位)。
orig_box = BoundingBoxes(
[
[860.0, 1100, 570, 1840, -7],
],
format="CXCYWHR",
canvas_size=(orig_img.size[1], orig_img.size[0]),
)
plot([(orig_img, orig_box)], bbox_width=10)

变换说明¶
rotater = v2.RandomRotation(degrees=(0, 180), expand=True)
rotated_imgs = [rotater((orig_img, orig_box)) for _ in range(4)]
plot([(orig_img, orig_box)] + rotated_imgs, bbox_width=10)

使用 Pad
padded_imgs_and_boxes = [
v2.Pad(padding=padding)(orig_img, orig_box)
for padding in (30, 50, 100, 200)
]
plot([(orig_img, orig_box)] + padded_imgs_and_boxes, bbox_width=10)

使用 Resize
resized_imgs = [
v2.Resize(size=size)(orig_img, orig_box)
for size in (30, 50, 100, orig_img.size)
]
plot([(orig_img, orig_box)] + resized_imgs, bbox_width=5)

请注意,在像素较少的图像中看起来更大的边界框是一种伪影,而非真实情况。那仅仅是边界框边界的栅格化表示,由于我们指定了该栅格线的固定宽度,因此显得更大。例如,当图像只有 30 像素宽时,3 像素宽的线相对较大。
裁剪模式及其对变换的影响¶
某些变换,例如 CenterCrop
,可能导致变换后的边界框部分超出变换后的(裁剪的)图像。通常,这可能发生在大多数 几何变换 上。
在这种情况下,边界框会根据其 clamping_mode
属性裁剪到变换后的图像大小。 clamping_mode
有三种值,决定了变换后边界框的裁剪方式。
None
:不应用裁剪,边界框可能部分超出图像。“hard”:边界框被裁剪到图像大小,使其所有角点都位于图像画布内。这可能会导致信息丢失,并可能导致不直观的结果。但对于某些应用程序可能是必需的,例如,如果模型不支持图像外部的边界框。
“soft”:这是
None
和“hard”之间的一种中间模式:边界框被裁剪,但不如“hard”模式严格。某些边界框尺寸可能仍超出图像。这是构造BoundingBoxes
时的默认值。
注意
对于轴对齐边界框,“soft” 和 “hard” 模式的行为相同,因为边界框始终被裁剪到图像大小。
让我们用 CenterCrop
变换来说明裁剪模式。
assert orig_box.clamping_mode == "soft"
box_hard_clamping = BoundingBoxes(orig_box, format=orig_box.format, canvas_size=orig_box.canvas_size, clamping_mode="hard")
box_no_clamping = BoundingBoxes(orig_box, format=orig_box.format, canvas_size=orig_box.canvas_size, clamping_mode=None)
crop_sizes = (800, 1200, 2000, orig_img.size)
soft_center_crops_and_boxes = [
v2.CenterCrop(size=size)(orig_img, orig_box)
for size in crop_sizes
]
hard_center_crops_and_boxes = [
v2.CenterCrop(size=size)(orig_img, box_hard_clamping)
for size in crop_sizes
]
no_clamping_center_crops_and_boxes = [
v2.CenterCrop(size=size)(orig_img, box_no_clamping)
for size in crop_sizes
]
plot([[(orig_img, box_hard_clamping)] + hard_center_crops_and_boxes,
[(orig_img, orig_box)] + soft_center_crops_and_boxes,
[(orig_img, box_no_clamping)] + no_clamping_center_crops_and_boxes],
bbox_width=10)

上面的图显示了“hard”裁剪模式、“soft”模式和 None
模式,顺序依次是。虽然“soft”和 None
模式的结果图相似,但它们不会产生完全相同的裁剪边界框。未裁剪的边界框将显示尺寸偏离图像的情况。
print("boxes with soft clamping:")
print(soft_center_crops_and_boxes)
print()
print("boxes with no clamping:")
print(no_clamping_center_crops_and_boxes)
boxes with soft clamping:
[(<PIL.Image.Image image mode=RGB size=800x800 at 0x7F7F333DA6B0>, BoundingBoxes([[478.8188, 400.9185, 570.0000, 874.1443, -7.0000]], format=BoundingBoxFormat.CXCYWHR, canvas_size=(800, 800), clamping_mode=soft)), (<PIL.Image.Image image mode=RGB size=1200x1200 at 0x7F7F333DA770>, BoundingBoxes([[ 678.9319, 600.0001, 569.9992, 1278.9989, -7.0000]], format=BoundingBoxFormat.CXCYWHR, canvas_size=(1200, 1200), clamping_mode=soft)), (<PIL.Image.Image image mode=RGB size=2000x2000 at 0x7F7F333DA860>, BoundingBoxes([[1089.0000, 918.0000, 570.0001, 1840.0000, -7.0000]], format=BoundingBoxFormat.CXCYWHR, canvas_size=(2000, 2000), clamping_mode=soft)), (<PIL.Image.Image image mode=RGB size=2364x1542 at 0x7F7F333DA920>, BoundingBoxes([[1260.9314, 771.0001, 570.0002, 1623.5675, -7.0000]], format=BoundingBoxFormat.CXCYWHR, canvas_size=(1542, 2364), clamping_mode=soft))]
boxes with no clamping:
[(<PIL.Image.Image image mode=RGB size=800x800 at 0x7F7F333DA350>, BoundingBoxes([[ 489., 318., 570., 1840., -7.]], format=BoundingBoxFormat.CXCYWHR, canvas_size=(800, 800), clamping_mode=None)), (<PIL.Image.Image image mode=RGB size=1200x1200 at 0x7F7F333DA290>, BoundingBoxes([[ 689., 518., 570., 1840., -7.]], format=BoundingBoxFormat.CXCYWHR, canvas_size=(1200, 1200), clamping_mode=None)), (<PIL.Image.Image image mode=RGB size=2000x2000 at 0x7F7F333DA1A0>, BoundingBoxes([[1089., 918., 570., 1840., -7.]], format=BoundingBoxFormat.CXCYWHR, canvas_size=(2000, 2000), clamping_mode=None)), (<PIL.Image.Image image mode=RGB size=2364x1542 at 0x7F7F333DA0E0>, BoundingBoxes([[1271., 689., 570., 1840., -7.]], format=BoundingBoxFormat.CXCYWHR, canvas_size=(1542, 2364), clamping_mode=None))]
设置裁剪模式¶
决定应用于边界框的裁剪策略的 clamping_mode
属性可以通过多种方式设置:
在使用其
BoundingBoxes
构造函数创建边界框时,如上面的示例所示。通过直接在现有实例上设置该属性,例如
boxes.clamping_mode = "hard"
。通过调用
SetClampingMode
变换。
另外,请记住,您始终可以通过调用 ClampBoundingBoxes()
变换来手动裁剪边界框!这是一个说明所有这些选项的示例。
t = v2.Compose([
v2.CenterCrop(size=(800,)), # clamps according to the current clamping_mode
# attribute, in this case set by the constructor
v2.SetClampingMode(None), # sets the clamping_mode attribute for future transforms
v2.Pad(padding=3), # clamps according to the current clamping_mode
# i.e. ``None``
v2.ClampBoundingBoxes(clamping_mode="soft"), # clamps with "soft" mode.
])
out_img, out_box = t(orig_img, orig_box)
plot([(orig_img, orig_box), (out_img, out_box)], bbox_width=10)

脚本总运行时间: (0 分钟 6.887 秒)