|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
3 |
| -from typing import Any, Tuple, Union, Optional |
| 3 | +from typing import Any, List, Tuple, Union, Optional, Sequence |
4 | 4 |
|
5 | 5 | import torch
|
6 | 6 | from torchvision._utils import StrEnum
|
| 7 | +from torchvision.transforms import InterpolationMode |
7 | 8 |
|
8 | 9 | from ._feature import _Feature
|
9 | 10 |
|
@@ -69,3 +70,142 @@ def to_format(self, format: Union[str, BoundingBoxFormat]) -> BoundingBox:
|
69 | 70 | return BoundingBox.new_like(
|
70 | 71 | self, convert_bounding_box_format(self, old_format=self.format, new_format=format), format=format
|
71 | 72 | )
|
| 73 | + |
| 74 | + def horizontal_flip(self) -> BoundingBox: |
| 75 | + from torchvision.prototype.transforms import functional as _F |
| 76 | + |
| 77 | + output = _F.horizontal_flip_bounding_box(self, format=self.format, image_size=self.image_size) |
| 78 | + return BoundingBox.new_like(self, output) |
| 79 | + |
| 80 | + def vertical_flip(self) -> BoundingBox: |
| 81 | + from torchvision.prototype.transforms import functional as _F |
| 82 | + |
| 83 | + output = _F.vertical_flip_bounding_box(self, format=self.format, image_size=self.image_size) |
| 84 | + return BoundingBox.new_like(self, output) |
| 85 | + |
| 86 | + def resize( # type: ignore[override] |
| 87 | + self, |
| 88 | + size: List[int], |
| 89 | + interpolation: InterpolationMode = InterpolationMode.BILINEAR, |
| 90 | + max_size: Optional[int] = None, |
| 91 | + antialias: bool = False, |
| 92 | + ) -> BoundingBox: |
| 93 | + from torchvision.prototype.transforms import functional as _F |
| 94 | + |
| 95 | + output = _F.resize_bounding_box(self, size, image_size=self.image_size, max_size=max_size) |
| 96 | + image_size = (size[0], size[0]) if len(size) == 1 else (size[0], size[1]) |
| 97 | + return BoundingBox.new_like(self, output, image_size=image_size, dtype=output.dtype) |
| 98 | + |
| 99 | + def crop(self, top: int, left: int, height: int, width: int) -> BoundingBox: |
| 100 | + from torchvision.prototype.transforms import functional as _F |
| 101 | + |
| 102 | + output = _F.crop_bounding_box(self, self.format, top, left) |
| 103 | + return BoundingBox.new_like(self, output, image_size=(height, width)) |
| 104 | + |
| 105 | + def center_crop(self, output_size: List[int]) -> BoundingBox: |
| 106 | + from torchvision.prototype.transforms import functional as _F |
| 107 | + |
| 108 | + output = _F.center_crop_bounding_box( |
| 109 | + self, format=self.format, output_size=output_size, image_size=self.image_size |
| 110 | + ) |
| 111 | + image_size = (output_size[0], output_size[0]) if len(output_size) == 1 else (output_size[0], output_size[1]) |
| 112 | + return BoundingBox.new_like(self, output, image_size=image_size) |
| 113 | + |
| 114 | + def resized_crop( |
| 115 | + self, |
| 116 | + top: int, |
| 117 | + left: int, |
| 118 | + height: int, |
| 119 | + width: int, |
| 120 | + size: List[int], |
| 121 | + interpolation: InterpolationMode = InterpolationMode.BILINEAR, |
| 122 | + antialias: bool = False, |
| 123 | + ) -> BoundingBox: |
| 124 | + from torchvision.prototype.transforms import functional as _F |
| 125 | + |
| 126 | + output = _F.resized_crop_bounding_box(self, self.format, top, left, height, width, size=size) |
| 127 | + image_size = (size[0], size[0]) if len(size) == 1 else (size[0], size[1]) |
| 128 | + return BoundingBox.new_like(self, output, image_size=image_size, dtype=output.dtype) |
| 129 | + |
| 130 | + def pad( |
| 131 | + self, padding: List[int], fill: Union[int, float, Sequence[float]] = 0, padding_mode: str = "constant" |
| 132 | + ) -> BoundingBox: |
| 133 | + from torchvision.prototype.transforms import functional as _F |
| 134 | + |
| 135 | + if padding_mode not in ["constant"]: |
| 136 | + raise ValueError(f"Padding mode '{padding_mode}' is not supported with bounding boxes") |
| 137 | + |
| 138 | + output = _F.pad_bounding_box(self, padding, format=self.format) |
| 139 | + |
| 140 | + # Update output image size: |
| 141 | + # TODO: remove the import below and make _parse_pad_padding available |
| 142 | + from torchvision.transforms.functional_tensor import _parse_pad_padding |
| 143 | + |
| 144 | + left, top, right, bottom = _parse_pad_padding(padding) |
| 145 | + height, width = self.image_size |
| 146 | + height += top + bottom |
| 147 | + width += left + right |
| 148 | + |
| 149 | + return BoundingBox.new_like(self, output, image_size=(height, width)) |
| 150 | + |
| 151 | + def rotate( |
| 152 | + self, |
| 153 | + angle: float, |
| 154 | + interpolation: InterpolationMode = InterpolationMode.NEAREST, |
| 155 | + expand: bool = False, |
| 156 | + fill: Optional[List[float]] = None, |
| 157 | + center: Optional[List[float]] = None, |
| 158 | + ) -> BoundingBox: |
| 159 | + from torchvision.prototype.transforms import functional as _F |
| 160 | + |
| 161 | + output = _F.rotate_bounding_box( |
| 162 | + self, format=self.format, image_size=self.image_size, angle=angle, expand=expand, center=center |
| 163 | + ) |
| 164 | + # TODO: update output image size if expand is True |
| 165 | + if expand: |
| 166 | + raise RuntimeError("Not yet implemented") |
| 167 | + return BoundingBox.new_like(self, output, dtype=output.dtype) |
| 168 | + |
| 169 | + def affine( |
| 170 | + self, |
| 171 | + angle: float, |
| 172 | + translate: List[float], |
| 173 | + scale: float, |
| 174 | + shear: List[float], |
| 175 | + interpolation: InterpolationMode = InterpolationMode.NEAREST, |
| 176 | + fill: Optional[List[float]] = None, |
| 177 | + center: Optional[List[float]] = None, |
| 178 | + ) -> BoundingBox: |
| 179 | + from torchvision.prototype.transforms import functional as _F |
| 180 | + |
| 181 | + output = _F.affine_bounding_box( |
| 182 | + self, |
| 183 | + self.format, |
| 184 | + self.image_size, |
| 185 | + angle, |
| 186 | + translate=translate, |
| 187 | + scale=scale, |
| 188 | + shear=shear, |
| 189 | + center=center, |
| 190 | + ) |
| 191 | + return BoundingBox.new_like(self, output, dtype=output.dtype) |
| 192 | + |
| 193 | + def perspective( |
| 194 | + self, |
| 195 | + perspective_coeffs: List[float], |
| 196 | + interpolation: InterpolationMode = InterpolationMode.BILINEAR, |
| 197 | + fill: Optional[List[float]] = None, |
| 198 | + ) -> BoundingBox: |
| 199 | + from torchvision.prototype.transforms import functional as _F |
| 200 | + |
| 201 | + output = _F.perspective_bounding_box(self, self.format, perspective_coeffs) |
| 202 | + return BoundingBox.new_like(self, output, dtype=output.dtype) |
| 203 | + |
| 204 | + def erase(self, i: int, j: int, h: int, w: int, v: torch.Tensor) -> BoundingBox: |
| 205 | + raise TypeError("Erase transformation does not support bounding boxes") |
| 206 | + |
| 207 | + def mixup(self, lam: float) -> BoundingBox: |
| 208 | + raise TypeError("Mixup transformation does not support bounding boxes") |
| 209 | + |
| 210 | + def cutmix(self, box: Tuple[int, int, int, int], lam_adjusted: float) -> BoundingBox: |
| 211 | + raise TypeError("Cutmix transformation does not support bounding boxes") |
0 commit comments