Files
roboflow--rf-detr/tests/datasets/test_coco_resize_config.py
T
wehub-resource-sync 16031aae96
CPU tests Workflow / Testing (ubuntu-latest, 3.12) (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.13) (push) Failing after 0s
Mypy Type Check / Type Check (push) Failing after 0s
Docs/Test WorkFlow / Test docs build (push) Failing after 1s
PR Conflict Labeler / labeling (push) Failing after 1s
Dependency resolution / Resolve [tflite] extra — Python 3.12 (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.10) (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.13) (push) Failing after 1s
CPU tests Workflow / build-pkg (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.10) (push) Failing after 0s
CPU tests Workflow / Testing (ubuntu-latest, 3.11) (push) Failing after 0s
Smoke Tests / try-all-models (macos-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (macos-latest, 3.13) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / testing-guardian (push) Has been cancelled
GPU tests Workflow / Testing (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:26:24 +08:00

268 lines
11 KiB
Python

# ------------------------------------------------------------------------
# RF-DETR
# Copyright (c) 2025 Roboflow. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 [see LICENSE for details]
# ------------------------------------------------------------------------
"""Characterization tests for _build_train_resize_config."""
import pytest
from rfdetr.datasets.coco import _build_train_resize_config
class TestBuildTrainResizeConfigStructure:
"""Top-level structure is always a single-element list wrapping a OneOf."""
@pytest.mark.parametrize(
"scales,square",
[
pytest.param([640], True, id="square-single"),
pytest.param([480, 640], True, id="square-multi"),
pytest.param([640], False, id="nonsquare-single"),
pytest.param([480, 640], False, id="nonsquare-multi"),
],
)
def test_returns_single_element_list(self, scales, square):
result = _build_train_resize_config(scales, square=square)
assert isinstance(result, list)
assert len(result) == 1
@pytest.mark.parametrize(
"scales,square",
[
pytest.param([640], True, id="square-single"),
pytest.param([480, 640], True, id="square-multi"),
pytest.param([640], False, id="nonsquare-single"),
pytest.param([480, 640], False, id="nonsquare-multi"),
],
)
def test_top_level_is_oneof_with_two_branches(self, scales, square):
result = _build_train_resize_config(scales, square=square)
entry = result[0]
assert "OneOf" in entry
oneof = entry["OneOf"]
assert len(oneof["transforms"]) == 2
class TestBuildTrainResizeConfigSquareSingleScale:
"""Square=True, single scale — OneOf[Resize] + Sequential[..., OneOf[RandomSizedCrop]]."""
def test_option_a_is_oneof_wrapping_single_resize(self):
result = _build_train_resize_config([640], square=True)
option_a = result[0]["OneOf"]["transforms"][0]
assert option_a == {
"OneOf": {
"transforms": [{"Resize": {"height": 640, "width": 640}}],
}
}
def test_option_b_is_sequential_with_oneof_crop(self):
result = _build_train_resize_config([640], square=True)
option_b = result[0]["OneOf"]["transforms"][1]
assert option_b == {
"Sequential": {
"transforms": [
{"SmallestMaxSize": {"max_size": [400, 500, 600]}},
{
"OneOf": {
"transforms": [
{"RandomSizedCrop": {"min_max_height": [384, 600], "height": 640, "width": 640}},
],
}
},
]
}
}
def test_uses_correct_scale_value(self):
result = _build_train_resize_config([480], square=True)
option_a = result[0]["OneOf"]["transforms"][0]
assert option_a == {
"OneOf": {
"transforms": [{"Resize": {"height": 480, "width": 480}}],
}
}
class TestBuildTrainResizeConfigSquareMultiScale:
"""Square=True, multiple scales — OneOf[Resize] + Sequential[..., OneOf[RandomSizedCrop]]."""
def test_option_a_is_oneof_of_resizes(self):
result = _build_train_resize_config([480, 640], square=True)
option_a = result[0]["OneOf"]["transforms"][0]
assert option_a == {
"OneOf": {
"transforms": [
{"Resize": {"height": 480, "width": 480}},
{"Resize": {"height": 640, "width": 640}},
],
}
}
def test_option_b_is_sequential_with_oneof_crop(self):
result = _build_train_resize_config([480, 640], square=True)
option_b = result[0]["OneOf"]["transforms"][1]
assert option_b == {
"Sequential": {
"transforms": [
{"SmallestMaxSize": {"max_size": [400, 500, 600]}},
{
"OneOf": {
"transforms": [
{"RandomSizedCrop": {"min_max_height": [384, 600], "height": 480, "width": 480}},
{"RandomSizedCrop": {"min_max_height": [384, 600], "height": 640, "width": 640}},
],
}
},
]
}
}
def test_three_scales_produce_three_resize_options(self):
result = _build_train_resize_config([384, 512, 640], square=True)
option_a = result[0]["OneOf"]["transforms"][0]
assert len(option_a["OneOf"]["transforms"]) == 3
class TestBuildTrainResizeConfigNonSquareSingleScale:
"""Square=False, single scale — SmallestMaxSize uses scalar, default cap 1333."""
def test_option_a_uses_scalar_size(self):
result = _build_train_resize_config([640], square=False)
option_a = result[0]["OneOf"]["transforms"][0]
assert option_a == {
"Sequential": {
"transforms": [
{"SmallestMaxSize": {"max_size": 640}},
{"LongestMaxSize": {"max_size": 1333}},
]
}
}
def test_option_b_uses_scalar_size(self):
result = _build_train_resize_config([640], square=False)
option_b = result[0]["OneOf"]["transforms"][1]
assert option_b == {
"Sequential": {
"transforms": [
{"SmallestMaxSize": {"max_size": [400, 500, 600]}},
{
"OneOf": {
"transforms": [
{"RandomSizedCrop": {"min_max_height": [384, 600], "height": 640, "width": 640}},
]
}
},
]
}
}
def test_custom_max_size(self):
result = _build_train_resize_config([640], square=False, max_size=800)
option_a = result[0]["OneOf"]["transforms"][0]
assert option_a["Sequential"]["transforms"][1] == {"LongestMaxSize": {"max_size": 800}}
class TestBuildTrainResizeConfigNonSquareMultiScale:
"""Square=False, multiple scales — SmallestMaxSize uses list directly."""
def test_option_a_uses_list_size(self):
result = _build_train_resize_config([480, 640], square=False)
option_a = result[0]["OneOf"]["transforms"][0]
assert option_a == {
"Sequential": {
"transforms": [
{"SmallestMaxSize": {"max_size": [480, 640]}},
{"LongestMaxSize": {"max_size": 1333}},
]
}
}
def test_option_b_uses_list_size(self):
result = _build_train_resize_config([480, 640], square=False)
option_b = result[0]["OneOf"]["transforms"][1]
assert option_b == {
"Sequential": {
"transforms": [
{"SmallestMaxSize": {"max_size": [400, 500, 600]}},
{
"OneOf": {
"transforms": [
{"RandomSizedCrop": {"min_max_height": [384, 600], "height": 480, "width": 480}},
{"RandomSizedCrop": {"min_max_height": [384, 600], "height": 640, "width": 640}},
]
}
},
]
}
}
def test_custom_max_size_applies_to_option_a_only(self):
"""max_size caps option_a's long side; option_b now resizes the crop directly to the target (no cap step)."""
result = _build_train_resize_config([480, 640], square=False, max_size=1000)
option_a = result[0]["OneOf"]["transforms"][0]
option_b_steps = result[0]["OneOf"]["transforms"][1]["Sequential"]["transforms"]
assert option_a["Sequential"]["transforms"][1] == {"LongestMaxSize": {"max_size": 1000}}
assert not any("LongestMaxSize" in step for step in option_b_steps)
class TestBuildTrainResizeConfigNonSquareScaleJitter:
"""Non-square option_b must keep RandomSizedCrop (scale jitter), not a fixed RandomCrop.
Regression tests for https://github.com/roboflow/rf-detr/issues/1018 — PR #752 replaced RandomSizeCrop(384, 600)
with a fixed RandomCrop(384, 384), silently removing scale jitter from the non-square training pipeline.
The ``fix-resize-crop`` branch keeps RandomSizedCrop and removes the wasteful fixed-384 intermediate hop: the crop
now resizes directly to the target scale (per-scale ``OneOf``, mirroring the square path). ``min_max_height`` uses
``[384, 600]`` to match the full SmallestMaxSize range — when the image short side is 400, albumentations clamps
the crop to the image height (a full-image crop), which is the original DETR recipe behaviour and preserves
zoom-out diversity across the SmallestMaxSize range.
"""
@pytest.mark.parametrize(
"scales",
[
pytest.param([640], id="nonsquare-single"),
pytest.param([480, 640], id="nonsquare-multi"),
],
)
def test_option_b_crop_step_uses_random_sized_crop(self, scales):
"""Non-square option_b crop must use RandomSizedCrop, never fixed RandomCrop (issue #1018)."""
result = _build_train_resize_config(scales, square=False)
option_b = result[0]["OneOf"]["transforms"][1]
crop_step = option_b["Sequential"]["transforms"][1]
crop_variants = crop_step["OneOf"]["transforms"]
assert crop_variants and all(
"RandomSizedCrop" in entry and "RandomCrop" not in entry for entry in crop_variants
)
@pytest.mark.parametrize(
"scales",
[
pytest.param([640], id="nonsquare-single"),
pytest.param([480, 640], id="nonsquare-multi"),
],
)
def test_option_b_crop_uses_full_scale_jitter_range(self, scales):
"""RandomSizedCrop min_max_height matches SmallestMaxSize range [384, 600] for full zoom-out diversity."""
result = _build_train_resize_config(scales, square=False)
option_b = result[0]["OneOf"]["transforms"][1]
crop_variants = option_b["Sequential"]["transforms"][1]["OneOf"]["transforms"]
assert all(entry["RandomSizedCrop"]["min_max_height"] == [384, 600] for entry in crop_variants)
@pytest.mark.parametrize(
"scales,square",
[
pytest.param([640], True, id="square-single"),
pytest.param([480, 640], True, id="square-multi"),
],
)
def test_square_option_b_unchanged(self, scales, square):
"""Square path must still use RandomSizedCrop parameterized by scale."""
result = _build_train_resize_config(scales, square=square)
option_b = result[0]["OneOf"]["transforms"][1]
inner_transforms = option_b["Sequential"]["transforms"][1]["OneOf"]["transforms"]
for entry in inner_transforms:
assert "RandomSizedCrop" in entry
assert entry["RandomSizedCrop"]["min_max_height"] == [384, 600]