Spaces:
Running on Zero
Running on Zero
Commit ·
2837b03
1
Parent(s): 56450ae
Extract dimension calc, add pytest, fix aspect ratio rounding
Browse files- Extract compute_output_dimensions() into dimensions.py with comment
explaining why round-to-8 is used instead of floor
- Add tests/test_dimensions.py (23 cases) covering multiples-of-8,
long-side pinning, aspect ratio error bound, and round-beats-floor
- Add pytest to requirements.txt
- app.py delegates to compute_output_dimensions()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- app.py +2 -7
- dimensions.py +15 -0
- requirements.txt +1 -0
- tests/conftest.py +4 -0
- tests/test_dimensions.py +99 -0
app.py
CHANGED
|
@@ -29,6 +29,7 @@ print("Using device:", device)
|
|
| 29 |
torch.backends.cuda.matmul.allow_tf32 = True
|
| 30 |
torch.backends.cudnn.allow_tf32 = True
|
| 31 |
|
|
|
|
| 32 |
from diffusers import FlowMatchEulerDiscreteScheduler
|
| 33 |
from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
|
| 34 |
from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
|
|
@@ -154,13 +155,7 @@ def update_dimensions_on_upload(image):
|
|
| 154 |
if image is None:
|
| 155 |
return MAX_OUTPUT_DIM, MAX_OUTPUT_DIM
|
| 156 |
w, h = image.size
|
| 157 |
-
|
| 158 |
-
nw = MAX_OUTPUT_DIM
|
| 159 |
-
nh = int(nw * h / w)
|
| 160 |
-
else:
|
| 161 |
-
nh = MAX_OUTPUT_DIM
|
| 162 |
-
nw = int(nh * w / h)
|
| 163 |
-
return (nw // 8) * 8, (nh // 8) * 8
|
| 164 |
|
| 165 |
|
| 166 |
@spaces.GPU
|
|
|
|
| 29 |
torch.backends.cuda.matmul.allow_tf32 = True
|
| 30 |
torch.backends.cudnn.allow_tf32 = True
|
| 31 |
|
| 32 |
+
from dimensions import compute_output_dimensions
|
| 33 |
from diffusers import FlowMatchEulerDiscreteScheduler
|
| 34 |
from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
|
| 35 |
from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
|
|
|
|
| 155 |
if image is None:
|
| 156 |
return MAX_OUTPUT_DIM, MAX_OUTPUT_DIM
|
| 157 |
w, h = image.size
|
| 158 |
+
return compute_output_dimensions(w, h)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
|
| 161 |
@spaces.GPU
|
dimensions.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MAX_OUTPUT_DIM = 2048
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def compute_output_dimensions(w, h, max_dim=MAX_OUTPUT_DIM):
|
| 5 |
+
# Pin the long side to max_dim and scale the short side proportionally.
|
| 6 |
+
# We snap to the nearest multiple of 8 (not floor) to minimise aspect ratio
|
| 7 |
+
# distortion: floor always undershoots, whereas rounding keeps the error
|
| 8 |
+
# within ±4px, which halves the worst-case ratio deviation.
|
| 9 |
+
if w > h:
|
| 10 |
+
nw = max_dim
|
| 11 |
+
nh = round(nw * h / w / 8) * 8
|
| 12 |
+
else:
|
| 13 |
+
nh = max_dim
|
| 14 |
+
nw = round(nh * w / h / 8) * 8
|
| 15 |
+
return nw, nh
|
requirements.txt
CHANGED
|
@@ -10,6 +10,7 @@ kernels
|
|
| 10 |
spaces
|
| 11 |
hf_xet
|
| 12 |
gradio
|
|
|
|
| 13 |
torch
|
| 14 |
numpy
|
| 15 |
av
|
|
|
|
| 10 |
spaces
|
| 11 |
hf_xet
|
| 12 |
gradio
|
| 13 |
+
pytest
|
| 14 |
torch
|
| 15 |
numpy
|
| 16 |
av
|
tests/conftest.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
tests/test_dimensions.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pytest
|
| 2 |
+
from dimensions import compute_output_dimensions, MAX_OUTPUT_DIM
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def aspect_ratio_error(w_in, h_in, w_out, h_out):
|
| 6 |
+
return abs(w_out / h_out - w_in / h_in)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
# --- output constraints ---
|
| 10 |
+
|
| 11 |
+
@pytest.mark.parametrize("w, h", [
|
| 12 |
+
(1920, 1080),
|
| 13 |
+
(1080, 1920),
|
| 14 |
+
(1000, 1000),
|
| 15 |
+
(1000, 381),
|
| 16 |
+
(381, 1000),
|
| 17 |
+
(3840, 2160),
|
| 18 |
+
(1, 1),
|
| 19 |
+
])
|
| 20 |
+
def test_output_is_multiple_of_8(w, h):
|
| 21 |
+
nw, nh = compute_output_dimensions(w, h)
|
| 22 |
+
assert nw % 8 == 0
|
| 23 |
+
assert nh % 8 == 0
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@pytest.mark.parametrize("w, h", [
|
| 27 |
+
(1920, 1080),
|
| 28 |
+
(1000, 381),
|
| 29 |
+
(3840, 2160),
|
| 30 |
+
])
|
| 31 |
+
def test_landscape_long_side_is_max_dim(w, h):
|
| 32 |
+
nw, nh = compute_output_dimensions(w, h)
|
| 33 |
+
assert nw == MAX_OUTPUT_DIM
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
@pytest.mark.parametrize("w, h", [
|
| 37 |
+
(1080, 1920),
|
| 38 |
+
(381, 1000),
|
| 39 |
+
])
|
| 40 |
+
def test_portrait_long_side_is_max_dim(w, h):
|
| 41 |
+
nw, nh = compute_output_dimensions(w, h)
|
| 42 |
+
assert nh == MAX_OUTPUT_DIM
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_square_stays_square():
|
| 46 |
+
nw, nh = compute_output_dimensions(1000, 1000)
|
| 47 |
+
assert nw == nh == MAX_OUTPUT_DIM
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
# --- aspect ratio preservation ---
|
| 51 |
+
|
| 52 |
+
@pytest.mark.parametrize("w, h", [
|
| 53 |
+
(1920, 1080),
|
| 54 |
+
(1080, 1920),
|
| 55 |
+
(1000, 381),
|
| 56 |
+
(381, 1000),
|
| 57 |
+
(3840, 2160),
|
| 58 |
+
])
|
| 59 |
+
def test_aspect_ratio_error_within_one_step(w, h):
|
| 60 |
+
nw, nh = compute_output_dimensions(w, h)
|
| 61 |
+
# Rounding to nearest-8 introduces at most 4px error on the short side.
|
| 62 |
+
# Ratio error = long * delta_short / short^2, so max = long * 4 / short^2.
|
| 63 |
+
max_allowed = max(nw, nh) * 4 / min(nw, nh) ** 2
|
| 64 |
+
assert aspect_ratio_error(w, h, nw, nh) <= max_allowed
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
@pytest.mark.parametrize("w, h", [
|
| 68 |
+
(1000, 381),
|
| 69 |
+
(381, 1000),
|
| 70 |
+
])
|
| 71 |
+
def test_round_beats_floor_on_tricky_ratio(w, h):
|
| 72 |
+
"""Cases where floor-to-8 and round-to-8 disagree: round must be at least as accurate."""
|
| 73 |
+
nw, nh = compute_output_dimensions(w, h)
|
| 74 |
+
# Reproduce floor-to-8 result for comparison
|
| 75 |
+
if w > h:
|
| 76 |
+
nh_floor = (int(MAX_OUTPUT_DIM * h / w) // 8) * 8
|
| 77 |
+
nw_floor = MAX_OUTPUT_DIM
|
| 78 |
+
else:
|
| 79 |
+
nw_floor = (int(MAX_OUTPUT_DIM * w / h) // 8) * 8
|
| 80 |
+
nh_floor = MAX_OUTPUT_DIM
|
| 81 |
+
assert aspect_ratio_error(w, h, nw, nh) <= aspect_ratio_error(w, h, nw_floor, nh_floor)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
# --- exact known values ---
|
| 85 |
+
|
| 86 |
+
def test_16_9_landscape():
|
| 87 |
+
nw, nh = compute_output_dimensions(1920, 1080)
|
| 88 |
+
assert (nw, nh) == (2048, 1152)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def test_16_9_portrait():
|
| 92 |
+
nw, nh = compute_output_dimensions(1080, 1920)
|
| 93 |
+
assert (nw, nh) == (1152, 2048)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def test_custom_max_dim():
|
| 97 |
+
nw, nh = compute_output_dimensions(1920, 1080, max_dim=1024)
|
| 98 |
+
assert nw == 1024
|
| 99 |
+
assert nh % 8 == 0
|