Skip to content

Commit c10a223

Browse files
NicolasHugfacebook-github-bot
authored andcommitted
[fbsync] clamping_mode: Rename 'none' to None and None to 'auto' (#9132)
Summary: Co-authored-by: Antoine Simoulin <[email protected]> Reviewed By: AntoineSimoulin Differential Revision: D79175061 fbshipit-source-id: 8917698aca2a4c1a2702c17196984c141079af70
1 parent 6b9d876 commit c10a223

File tree

4 files changed

+36
-35
lines changed

4 files changed

+36
-35
lines changed

test/test_transforms_v2.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2237,7 +2237,7 @@ def _reference_rotate_bounding_boxes(self, bounding_boxes, *, angle, expand, cen
22372237
@pytest.mark.parametrize("expand", [False, True])
22382238
@pytest.mark.parametrize("center", _CORRECTNESS_AFFINE_KWARGS["center"])
22392239
def test_functional_bounding_boxes_correctness(self, format, angle, expand, center):
2240-
bounding_boxes = make_bounding_boxes(format=format, clamping_mode="none")
2240+
bounding_boxes = make_bounding_boxes(format=format, clamping_mode=None)
22412241

22422242
actual = F.rotate(bounding_boxes, angle=angle, expand=expand, center=center)
22432243
expected = self._reference_rotate_bounding_boxes(bounding_boxes, angle=angle, expand=expand, center=center)
@@ -2249,7 +2249,7 @@ def test_functional_bounding_boxes_correctness(self, format, angle, expand, cent
22492249
@pytest.mark.parametrize("center", _CORRECTNESS_AFFINE_KWARGS["center"])
22502250
@pytest.mark.parametrize("seed", list(range(5)))
22512251
def test_transform_bounding_boxes_correctness(self, format, expand, center, seed):
2252-
bounding_boxes = make_bounding_boxes(format=format, clamping_mode="none")
2252+
bounding_boxes = make_bounding_boxes(format=format, clamping_mode=None)
22532253

22542254
transform = transforms.RandomRotation(**self._CORRECTNESS_TRANSFORM_AFFINE_RANGES, expand=expand, center=center)
22552255

@@ -4428,7 +4428,7 @@ def test_functional_bounding_boxes_correctness(self, format):
44284428
# _reference_resized_crop_bounding_boxes we are fusing the crop and the
44294429
# resize operation, where none of the croppings happen - particularly,
44304430
# the intermediate one.
4431-
bounding_boxes = make_bounding_boxes(self.INPUT_SIZE, format=format, clamping_mode="none")
4431+
bounding_boxes = make_bounding_boxes(self.INPUT_SIZE, format=format, clamping_mode=None)
44324432

44334433
actual = F.resized_crop(bounding_boxes, **self.CROP_KWARGS, size=self.OUTPUT_SIZE)
44344434
expected = self._reference_resized_crop_bounding_boxes(
@@ -5507,7 +5507,7 @@ def test_correctness_image(self, mean, std, dtype, fn):
55075507

55085508
class TestClampBoundingBoxes:
55095509
@pytest.mark.parametrize("format", list(tv_tensors.BoundingBoxFormat))
5510-
@pytest.mark.parametrize("clamping_mode", ("hard", "none")) # TODOBB add soft
5510+
@pytest.mark.parametrize("clamping_mode", ("hard", None)) # TODOBB add soft
55115511
@pytest.mark.parametrize("dtype", [torch.int64, torch.float32])
55125512
@pytest.mark.parametrize("device", cpu_and_cuda())
55135513
def test_kernel(self, format, clamping_mode, dtype, device):
@@ -5521,7 +5521,7 @@ def test_kernel(self, format, clamping_mode, dtype, device):
55215521
)
55225522

55235523
@pytest.mark.parametrize("format", list(tv_tensors.BoundingBoxFormat))
5524-
@pytest.mark.parametrize("clamping_mode", ("hard", "none")) # TODOBB add soft
5524+
@pytest.mark.parametrize("clamping_mode", ("hard", None)) # TODOBB add soft
55255525
def test_functional(self, format, clamping_mode):
55265526
check_functional(F.clamp_bounding_boxes, make_bounding_boxes(format=format, clamping_mode=clamping_mode))
55275527

@@ -5531,7 +5531,7 @@ def test_errors(self):
55315531
format, canvas_size = input_tv_tensor.format, input_tv_tensor.canvas_size
55325532

55335533
for format_, canvas_size_, clamping_mode_ in itertools.product(
5534-
(format, None), (canvas_size, None), (input_tv_tensor.clamping_mode, None)
5534+
(format, None), (canvas_size, None), (input_tv_tensor.clamping_mode, "auto")
55355535
):
55365536
with pytest.raises(
55375537
ValueError,
@@ -5549,8 +5549,8 @@ def test_transform(self):
55495549
check_transform(transforms.ClampBoundingBoxes(), make_bounding_boxes())
55505550

55515551
@pytest.mark.parametrize("rotated", (True, False))
5552-
@pytest.mark.parametrize("constructor_clamping_mode", ("hard", "none"))
5553-
@pytest.mark.parametrize("clamping_mode", ("hard", "none", None)) # TODOBB add soft here.
5552+
@pytest.mark.parametrize("constructor_clamping_mode", ("hard", None))
5553+
@pytest.mark.parametrize("clamping_mode", ("hard", None, "auto")) # TODOBB add soft here.
55545554
@pytest.mark.parametrize("pass_pure_tensor", (True, False))
55555555
@pytest.mark.parametrize("fn", [F.clamp_bounding_boxes, transform_cls_to_functional(transforms.ClampBoundingBoxes)])
55565556
def test_clamping_mode(self, rotated, constructor_clamping_mode, clamping_mode, pass_pure_tensor, fn):
@@ -5559,15 +5559,15 @@ def test_clamping_mode(self, rotated, constructor_clamping_mode, clamping_mode,
55595559
# functional (or to the class) relies on the box's `.clamping_mode`
55605560
# attribute
55615561
# - That clamping happens when it should, and only when it should, i.e.
5562-
# when the clamping mode is not "none". It doesn't validate the
5563-
# nunmerical results, only that clamping happened. For that, we create
5562+
# when the clamping mode is not None. It doesn't validate the
5563+
# numerical results, only that clamping happened. For that, we create
55645564
# a large 100x100 box inside of a small 10x10 image.
55655565

55665566
if pass_pure_tensor and fn is not F.clamp_bounding_boxes:
55675567
# Only the functional supports pure tensors, not the class
55685568
return
5569-
if pass_pure_tensor and clamping_mode is None:
5570-
# cannot leave clamping_mode=None when passing pure tensor
5569+
if pass_pure_tensor and clamping_mode == "auto":
5570+
# cannot leave clamping_mode="auto" when passing pure tensor
55715571
return
55725572

55735573
if rotated:
@@ -5591,17 +5591,17 @@ def test_clamping_mode(self, rotated, constructor_clamping_mode, clamping_mode,
55915591
else:
55925592
out = fn(boxes, clamping_mode=clamping_mode)
55935593

5594-
clamping_mode_prevailing = constructor_clamping_mode if clamping_mode is None else clamping_mode
5595-
if clamping_mode_prevailing == "none":
5594+
clamping_mode_prevailing = constructor_clamping_mode if clamping_mode == "auto" else clamping_mode
5595+
if clamping_mode_prevailing is None:
55965596
assert_equal(boxes, out) # should be a pass-through
55975597
else:
55985598
assert_equal(out, expected_clamped_output)
55995599

56005600

56015601
class TestSetClampingMode:
56025602
@pytest.mark.parametrize("format", list(tv_tensors.BoundingBoxFormat))
5603-
@pytest.mark.parametrize("constructor_clamping_mode", ("hard", "none")) # TODOBB add soft
5604-
@pytest.mark.parametrize("desired_clamping_mode", ("hard", "none")) # TODOBB add soft
5603+
@pytest.mark.parametrize("constructor_clamping_mode", ("hard", None)) # TODOBB add soft
5604+
@pytest.mark.parametrize("desired_clamping_mode", ("hard", None)) # TODOBB add soft
56055605
def test_setter(self, format, constructor_clamping_mode, desired_clamping_mode):
56065606

56075607
in_boxes = make_bounding_boxes(format=format, clamping_mode=constructor_clamping_mode)
@@ -5611,7 +5611,7 @@ def test_setter(self, format, constructor_clamping_mode, desired_clamping_mode):
56115611
assert out_boxes.clamping_mode == desired_clamping_mode
56125612

56135613
@pytest.mark.parametrize("format", list(tv_tensors.BoundingBoxFormat))
5614-
@pytest.mark.parametrize("constructor_clamping_mode", ("hard", "none")) # TODOBB add soft
5614+
@pytest.mark.parametrize("constructor_clamping_mode", ("hard", None)) # TODOBB add soft
56155615
def test_pipeline_no_leak(self, format, constructor_clamping_mode):
56165616
class AssertClampingMode(transforms.Transform):
56175617
def __init__(self, expected_clamping_mode):
@@ -5626,12 +5626,12 @@ def transform(self, inpt, _):
56265626

56275627
t = transforms.Compose(
56285628
[
5629-
transforms.SetClampingMode("none"),
5630-
AssertClampingMode("none"),
5629+
transforms.SetClampingMode(None),
5630+
AssertClampingMode(None),
56315631
transforms.SetClampingMode("hard"),
56325632
AssertClampingMode("hard"),
5633-
transforms.SetClampingMode("none"),
5634-
AssertClampingMode("none"),
5633+
transforms.SetClampingMode(None),
5634+
AssertClampingMode(None),
56355635
transforms.ClampBoundingBoxes("hard"),
56365636
]
56375637
)
@@ -5643,7 +5643,7 @@ def transform(self, inpt, _):
56435643

56445644
# assert that the output boxes clamping_mode is the one set by the last SetClampingMode.
56455645
# ClampBoundingBoxes doesn't set clamping_mode.
5646-
assert out_boxes.clamping_mode == "none"
5646+
assert out_boxes.clamping_mode is None
56475647

56485648

56495649
class TestClampKeyPoints:

torchvision/transforms/v2/_meta.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Optional, Union
1+
from typing import Any, Union
22

33
from torchvision import tv_tensors
44
from torchvision.transforms.v2 import functional as F, Transform
@@ -34,7 +34,9 @@ class ClampBoundingBoxes(Transform):
3434
3535
"""
3636

37-
def __init__(self, clamping_mode: Optional[CLAMPING_MODE_TYPE] = None) -> None:
37+
# TODOBB consider "auto" to be a Literal, make sur torchscript is still happy
38+
# TODOBB validate clamping_mode
39+
def __init__(self, clamping_mode: Union[CLAMPING_MODE_TYPE, str] = "auto") -> None:
3840
super().__init__()
3941
self.clamping_mode = clamping_mode
4042

torchvision/transforms/v2/functional/_meta.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional
1+
from typing import Optional, Union
22

33
import PIL.Image
44
import torch
@@ -376,7 +376,7 @@ def _clamp_bounding_boxes(
376376
canvas_size: tuple[int, int],
377377
clamping_mode: CLAMPING_MODE_TYPE,
378378
) -> torch.Tensor:
379-
if clamping_mode is not None and clamping_mode == "none":
379+
if clamping_mode is None:
380380
return bounding_boxes.clone()
381381
# TODO: Investigate if it makes sense from a performance perspective to have an implementation for every
382382
# BoundingBoxFormat instead of converting back and forth
@@ -479,7 +479,7 @@ def _clamp_y_intercept(
479479
b1 = b2.clamp(b1, b3).clamp(0, canvas_size[0])
480480
b4 = b3.clamp(b2, b4).clamp(0, canvas_size[0])
481481

482-
if clamping_mode == "hard":
482+
if clamping_mode is not None and clamping_mode == "hard":
483483
# Hard clamping: Average b1 and b4, and adjust b2 and b3 for maximum area
484484
b1 = b4 = (b1 + b4) / 2
485485

@@ -574,7 +574,7 @@ def _clamp_along_y_axis(
574574
[case_a, case_b, case_c],
575575
):
576576
bounding_boxes = torch.where(cond.unsqueeze(1).repeat(1, 8), case.reshape(-1, 8), bounding_boxes)
577-
if clamping_mode == "hard":
577+
if clamping_mode is not None and clamping_mode == "hard":
578578
bounding_boxes[..., 0].clamp_(0) # Clamp x1 to 0
579579

580580
if need_cast:
@@ -610,7 +610,7 @@ def _clamp_rotated_bounding_boxes(
610610
Returns:
611611
torch.Tensor: Clamped bounding boxes in the original format and shape
612612
"""
613-
if clamping_mode is not None and clamping_mode == "none":
613+
if clamping_mode is None:
614614
return bounding_boxes.clone()
615615
original_shape = bounding_boxes.shape
616616
dtype = bounding_boxes.dtype
@@ -657,15 +657,15 @@ def clamp_bounding_boxes(
657657
inpt: torch.Tensor,
658658
format: Optional[BoundingBoxFormat] = None,
659659
canvas_size: Optional[tuple[int, int]] = None,
660-
clamping_mode: Optional[CLAMPING_MODE_TYPE] = None,
660+
clamping_mode: Union[CLAMPING_MODE_TYPE, str] = "auto",
661661
) -> torch.Tensor:
662662
"""See :func:`~torchvision.transforms.v2.ClampBoundingBoxes` for details."""
663663
if not torch.jit.is_scripting():
664664
_log_api_usage_once(clamp_bounding_boxes)
665665

666666
if torch.jit.is_scripting() or is_pure_tensor(inpt):
667667

668-
if format is None or canvas_size is None or clamping_mode is None:
668+
if format is None or canvas_size is None or (clamping_mode is not None and clamping_mode == "auto"):
669669
raise ValueError("For pure tensor inputs, `format`, `canvas_size` and `clamping_mode` have to be passed.")
670670
if tv_tensors.is_rotated_bounding_format(format):
671671
return _clamp_rotated_bounding_boxes(
@@ -676,7 +676,7 @@ def clamp_bounding_boxes(
676676
elif isinstance(inpt, tv_tensors.BoundingBoxes):
677677
if format is not None or canvas_size is not None:
678678
raise ValueError("For bounding box tv_tensor inputs, `format` and `canvas_size` must not be passed.")
679-
if clamping_mode is None:
679+
if clamping_mode is not None and clamping_mode == "auto":
680680
clamping_mode = inpt.clamping_mode
681681
if tv_tensors.is_rotated_bounding_format(inpt.format):
682682
output = _clamp_rotated_bounding_boxes(

torchvision/tv_tensors/_bounding_boxes.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from collections.abc import Mapping, Sequence
44

55
from enum import Enum
6-
from typing import Any
6+
from typing import Any, Optional
77

88
import torch
99
from torch.utils._pytree import tree_flatten
@@ -48,8 +48,7 @@ def is_rotated_bounding_format(format: BoundingBoxFormat) -> bool:
4848

4949
# TODOBB consider making this a Literal instead. Tried briefly and got
5050
# torchscript errors, leaving to str for now.
51-
# CLAMPING_MODE_TYPE = Literal["hard", "soft", "none"]
52-
CLAMPING_MODE_TYPE = str
51+
CLAMPING_MODE_TYPE = Optional[str]
5352

5453
# TODOBB All docs. Add any new API to rst files, add tutorial[s].
5554

0 commit comments

Comments
 (0)