feat: bucket selection matching Klein 9B table

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 22:47:46 +02:00
parent 628a945514
commit cdd742c950
2 changed files with 58 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
"""Pure bucket math for KLEIN_BUCKET_SIZES.md. Stdlib only."""
import math
def pick_bucket(iw, ih, resolution=1280, divisible=64):
"""Choose the on-grid bucket (W,H), area <= resolution^2, nearest to the
image aspect (log distance; tie-break larger area)."""
budget = resolution * resolution
target = iw / ih
best = None
w = divisible
w_max = budget // divisible
while w <= w_max:
h = (budget // w // divisible) * divisible # largest on-grid h within budget
if h >= divisible:
err = abs(math.log(w / h) - math.log(target))
cand = (err, -(w * h), w, h) # min err, then max area
if best is None or cand < best:
best = cand
w += divisible
return best[2], best[3]
def cover_crop_params(iw, ih, W, H):
"""Cover-scale + centered crop to land (iw,ih) exactly on (W,H)."""
scale = max(W / iw, H / ih)
new_w = max(W, round(iw * scale))
new_h = max(H, round(ih * scale))
left = (new_w - W) // 2
top = (new_h - H) // 2
return new_w, new_h, left, top, scale
+27
View File
@@ -0,0 +1,27 @@
from gates import buckets
# (iw, ih) -> expected (W, H) from KLEIN_BUCKET_SIZES.md, budget 1280, ÷64
CASES = [
(1000, 1000, 1280, 1280), # square
(1000, 2000, 896, 1792), # a=0.50 portrait
(1000, 1730, 960, 1664), # a≈0.58
(1000, 1100, 1216, 1344), # a≈0.90 -> portrait-leaning
(2000, 1000, 1792, 896), # a=2.00 landscape
(1500, 1000, 1536, 1024), # a=1.50
]
def test_pick_bucket_matches_table():
for iw, ih, W, H in CASES:
assert buckets.pick_bucket(iw, ih, 1280, 64) == (W, H)
def test_buckets_are_on_grid_and_within_budget():
for iw, ih, *_ in CASES:
W, H = buckets.pick_bucket(iw, ih, 1280, 64)
assert W % 64 == 0 and H % 64 == 0
assert W * H <= 1280 * 1280
def test_square_is_exactly_1280():
assert buckets.pick_bucket(512, 512, 1280, 64) == (1280, 1280)