feat(crops): shared crop primitive for the region/crop pipeline (#114)
The trunk of both crop jobs — CCIP figure-crops and SigLIP concept-crops call the SAME crop_region(): normalized-bbox crop with optional context padding, edge-clamping, and the lower-bound size floor (max of a fraction-of-short-side and an absolute pixel floor) below which a region is too small to embed and returns None. Only the proposer (where) and embedder (what) differ; the crop is shared. Pure Pillow — importable + testable anywhere (the GPU agent imports it for the crop step). Unit-lane tests (no DB): region pixels, floor rejection, edge clamp, pad expansion, out-size resize. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
"""Shared crop primitive (#114) — pure Pillow, no DB, so it runs in the fast
|
||||
unit lane (no integration marker)."""
|
||||
from PIL import Image
|
||||
|
||||
from backend.app.services.ml.crops import crop_region
|
||||
|
||||
|
||||
def _quadrant_img():
|
||||
"""400x400 red with a blue bottom-right quadrant, so a crop's content is
|
||||
checkable by pixel."""
|
||||
img = Image.new("RGB", (400, 400), (255, 0, 0))
|
||||
img.paste(Image.new("RGB", (200, 200), (0, 0, 255)), (200, 200))
|
||||
return img
|
||||
|
||||
|
||||
def test_crop_returns_region_pixels():
|
||||
crop = crop_region(_quadrant_img(), (0.5, 0.5, 0.5, 0.5))
|
||||
assert crop is not None
|
||||
assert crop.size == (200, 200)
|
||||
assert crop.getpixel((100, 100)) == (0, 0, 255) # the blue quadrant
|
||||
|
||||
|
||||
def test_crop_below_floor_is_rejected():
|
||||
# 0.05 * 400 = 20px on a side — below max(64, 0.10*400=40) → None.
|
||||
assert crop_region(_quadrant_img(), (0.0, 0.0, 0.05, 0.05)) is None
|
||||
|
||||
|
||||
def test_crop_clamped_to_image_bounds():
|
||||
# Box runs off the right/bottom edge; clamps to the remaining 0.2*400=80px.
|
||||
crop = crop_region(_quadrant_img(), (0.8, 0.8, 0.5, 0.5))
|
||||
assert crop is not None
|
||||
assert crop.size == (80, 80)
|
||||
|
||||
|
||||
def test_pad_expands_the_crop():
|
||||
base = crop_region(_quadrant_img(), (0.4, 0.4, 0.2, 0.2))
|
||||
padded = crop_region(_quadrant_img(), (0.4, 0.4, 0.2, 0.2), pad=0.5)
|
||||
assert base.size == (80, 80)
|
||||
assert padded.size[0] > base.size[0] and padded.size[1] > base.size[1]
|
||||
|
||||
|
||||
def test_out_size_resizes_square():
|
||||
crop = crop_region(_quadrant_img(), (0.25, 0.25, 0.5, 0.5), out_size=224)
|
||||
assert crop.size == (224, 224)
|
||||
Reference in New Issue
Block a user