sprite / tests /test_background.py
Cnass's picture
Upload 5 files
ebd87b7 verified
Raw History Blame Contribute Delete
4.5 kB
"""
tests/test_background.py
--------------------------
Tests for border-connected background detection.
Run with: pytest tests/test_background.py -v
"""
import sys
from pathlib import Path
import numpy as np
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from background import apply_transparency, detect_background
def test_black_background_detected():
rgba = np.zeros((20, 20, 4), dtype=np.uint8)
rgba[:, :, 3] = 255
bg = detect_background(rgba)
assert bg.color[:3] == (0, 0, 0)
assert bg.is_transparent is False
def test_interior_black_pixel_is_not_marked_background():
"""
CRITICAL RULE: a sprite drawn on a black background can have its own
black pixels (e.g. an outline). Only background pixels reachable from
the sheet border are background -- an enclosed black pixel inside a
sprite must never be marked as background.
"""
rgba = np.zeros((20, 20, 4), dtype=np.uint8)
rgba[:, :, 3] = 255
rgba[5:15, 5:15] = [200, 50, 50, 255] # red sprite body
rgba[9, 9] = [0, 0, 0, 255] # black pixel INSIDE the sprite
bg = detect_background(rgba)
assert bg.mask[9, 9] == False # noqa: E712 -- must not be flagged as background
assert bg.mask[0, 0] == True # noqa: E712 -- the actual border IS background
def test_apply_transparency_preserves_interior_pixel():
rgba = np.zeros((20, 20, 4), dtype=np.uint8)
rgba[:, :, 3] = 255
rgba[5:15, 5:15] = [200, 50, 50, 255]
rgba[9, 9] = [0, 0, 0, 255]
bg = detect_background(rgba)
out = apply_transparency(rgba, bg)
assert out[9, 9, 3] == 255 # interior black pixel: untouched
assert out[0, 0, 3] == 0 # border background pixel: made transparent
assert tuple(out[9, 9, :3]) == (0, 0, 0) # color also untouched
def test_already_transparent_sheet_detected_as_transparent_background():
rgba = np.zeros((20, 20, 4), dtype=np.uint8) # fully transparent
rgba[5:15, 5:15] = [80, 180, 90, 255] # opaque sprite in the middle
bg = detect_background(rgba)
assert bg.is_transparent is True
assert bg.mask[0, 0] == True # noqa: E712
def test_white_background_detected():
rgba = np.full((20, 20, 4), 255, dtype=np.uint8)
rgba[5:15, 5:15] = [10, 10, 200, 255] # blue sprite
bg = detect_background(rgba)
assert bg.color[:3] == (255, 255, 255)
assert bg.mask[9, 9] == False # noqa: E712 -- sprite interior untouched
def test_colored_background_detected():
rgba = np.zeros((20, 20, 4), dtype=np.uint8)
rgba[:, :, 3] = 255
rgba[:, :, :3] = [30, 200, 30] # green background
rgba[5:15, 5:15] = [200, 30, 30, 255] # red sprite
bg = detect_background(rgba)
assert bg.color[:3] == (30, 200, 30)
assert bg.mask[9, 9] == False # noqa: E712
def test_tolerance_absorbs_near_background_noise():
"""
Slightly-off-black border pixels (e.g. from lossy JPEG compression
artifacts) should still be classified as background within tolerance.
"""
rng = np.random.default_rng(0)
rgba = np.zeros((20, 20, 4), dtype=np.uint8)
rgba[:, :, 3] = 255
jitter = rng.integers(-5, 6, size=(20, 20, 3))
rgba[:, :, :3] = np.clip(0 + jitter, 0, 255).astype(np.uint8)
rgba[5:15, 5:15] = [200, 50, 50, 255]
bg = detect_background(rgba)
# Most of the noisy near-black border should still register as background.
assert bg.mask[0, 0] == True # noqa: E712
assert bg.mask.sum() > 200 # most of the 400px sheet minus the sprite
def test_two_sprites_near_black_border_both_preserved():
"""Two separate sprites each containing a black pixel of their own."""
rgba = np.zeros((30, 30, 4), dtype=np.uint8)
rgba[:, :, 3] = 255
rgba[3:10, 3:10] = [80, 200, 90, 255]
rgba[6, 6] = [0, 0, 0, 255]
rgba[20:27, 20:27] = [90, 90, 220, 255]
rgba[23, 23] = [0, 0, 0, 255]
bg = detect_background(rgba)
assert bg.mask[6, 6] == False # noqa: E712
assert bg.mask[23, 23] == False # noqa: E712
if __name__ == "__main__":
import traceback
tests = [(name, fn) for name, fn in list(globals().items()) if name.startswith("test_") and callable(fn)]
passed, failed = 0, 0
for name, fn in tests:
try:
fn()
print(f"PASS {name}")
passed += 1
except Exception:
print(f"FAIL {name}")
traceback.print_exc()
failed += 1
print(f"\n{passed} passed, {failed} failed")
sys.exit(1 if failed else 0)