File size: 3,949 Bytes
1a12965
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""Unit tests for src.placement.placement_engine."""

from __future__ import annotations

import numpy as np

from src.config.schema import PlacementConfig
from src.placement.placement_engine import MIN_SIGNATURE_HEIGHT_PX, PlacementEngine
from src.utils.random_utils import SeedManager


def _synthetic_signature(w: int = 300, h: int = 150) -> np.ndarray:
    """A simple synthetic RGBA "signature": a diagonal opaque stroke on transparency."""
    rgba = np.zeros((h, w, 4), dtype=np.uint8)
    for x in range(w):
        y = int((x / w) * (h - 1))
        rgba[max(y - 3, 0):y + 3, x, :3] = (20, 20, 25)
        rgba[max(y - 3, 0):y + 3, x, 3] = 255
    return rgba


def test_placement_is_deterministic_for_same_seed():
    cfg = PlacementConfig(rotation_deg={"min": -5, "max": 5}, scale_factor={"min": 0.7, "max": 1.0})
    engine = PlacementEngine(cfg)
    sig = _synthetic_signature()

    r1 = engine.place(sig, target_box=(100, 100, 300, 200), canvas_size=(800, 600), seed_manager=SeedManager(1, 5))
    r2 = engine.place(sig, target_box=(100, 100, 300, 200), canvas_size=(800, 600), seed_manager=SeedManager(1, 5))

    assert r1.rotation_deg == r2.rotation_deg
    assert r1.scale_factor == r2.scale_factor
    assert r1.intended_bbox == r2.intended_bbox
    assert np.array_equal(r1.layer, r2.layer)


def test_placement_differs_for_different_sample_index():
    cfg = PlacementConfig(rotation_deg={"min": -5, "max": 5}, scale_factor={"min": 0.7, "max": 1.0})
    engine = PlacementEngine(cfg)
    sig = _synthetic_signature()

    r1 = engine.place(sig, target_box=(100, 100, 300, 200), canvas_size=(800, 600), seed_manager=SeedManager(1, 1))
    r2 = engine.place(sig, target_box=(100, 100, 300, 200), canvas_size=(800, 600), seed_manager=SeedManager(1, 2))

    assert r1.rotation_deg != r2.rotation_deg or r1.scale_factor != r2.scale_factor


def test_placement_respects_aspect_ratio_when_not_rotated():
    cfg = PlacementConfig(rotation_deg=0.0, scale_factor=0.5, margin=0.0)
    engine = PlacementEngine(cfg)
    sig = _synthetic_signature(w=300, h=150)  # aspect ratio 2:1

    result = engine.place(sig, target_box=(0, 0, 400, 400), canvas_size=(800, 800), seed_manager=SeedManager(1, 1))
    x1, y1, x2, y2 = result.intended_bbox
    placed_w, placed_h = x2 - x1, y2 - y1
    assert abs((placed_w / placed_h) - 2.0) < 0.05


def test_placement_output_layer_matches_canvas_size():
    cfg = PlacementConfig()
    engine = PlacementEngine(cfg)
    sig = _synthetic_signature()
    result = engine.place(sig, target_box=(10, 10, 200, 100), canvas_size=(640, 480), seed_manager=SeedManager(2, 0))
    assert result.layer.shape == (480, 640, 4)


def test_tiny_box_still_produces_a_legible_minimum_height_signature():
    cfg = PlacementConfig(rotation_deg=0.0, scale_factor=1.0, margin=0.05)
    engine = PlacementEngine(cfg)
    sig = _synthetic_signature(w=300, h=150)

    # A box far shorter than the natural signature height.
    result = engine.place(sig, target_box=(0, 0, 300, 6), canvas_size=(800, 800), seed_manager=SeedManager(1, 1))
    x1, y1, x2, y2 = result.intended_bbox
    assert (y2 - y1) >= MIN_SIGNATURE_HEIGHT_PX - 1  # allow rounding


def test_misplacement_never_triggers_when_probability_zero():
    cfg = PlacementConfig(misplacement_probability=0.0)
    engine = PlacementEngine(cfg)
    sig = _synthetic_signature()
    for i in range(20):
        result = engine.place(sig, target_box=(50, 50, 250, 150), canvas_size=(800, 600), seed_manager=SeedManager(1, i))
        assert result.misplaced is False


def test_misplacement_always_triggers_when_probability_one():
    cfg = PlacementConfig(misplacement_probability=1.0, misplacement_strength=0.5)
    engine = PlacementEngine(cfg)
    sig = _synthetic_signature()
    for i in range(10):
        result = engine.place(sig, target_box=(50, 50, 250, 150), canvas_size=(800, 600), seed_manager=SeedManager(1, i))
        assert result.misplaced is True