From a62012a3e12de05d637faf4c57f27b9e2bf610e1 Mon Sep 17 00:00:00 2001 From: ErenAta16 Date: Wed, 29 Jul 2026 16:27:54 +0300 Subject: [PATCH] Don't let _resize_and_fill/_resize_and_crop scale a side down to zero Both helpers compute the scaled size with floor division: src_w = width if ratio < src_ratio else image.width * height // image.height src_h = height if ratio >= src_ratio else image.height * width // image.width For a very thin image that rounds to 0. Fitting a 1x2000 image into 512x512 gives `1 * 512 // 2000 == 0`, and `Image.resize((0, 512))` fails with ValueError: height and width must be > 0 which names neither the input nor the side that vanished. 1x2000 -> 512x512 fill=ValueError crop=ok 2000x1 -> 512x512 fill=ValueError crop=ok 3x900 -> 512x512 fill=ok crop=ok `_resize_and_crop` survives these two only because the branch that would round to zero is the one it does not take for those aspect ratios; the same expression is there and the same collapse happens for the mirrored inputs. Clamping both to a minimum of one pixel in both helpers keeps them consistent. Reachable from the public API as `VaeImageProcessor.resize(image, height, width, resize_mode="fill")`. Sizes that already computed correctly are untouched: the clamp only fires where the old value was 0. --- src/diffusers/image_processor.py | 11 +++++++---- tests/others/test_image_processor.py | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/diffusers/image_processor.py b/src/diffusers/image_processor.py index 4f6f4bd52b9c..1a8ce4ccfb92 100644 --- a/src/diffusers/image_processor.py +++ b/src/diffusers/image_processor.py @@ -400,8 +400,11 @@ def _resize_and_fill( ratio = width / height src_ratio = image.width / image.height - src_w = width if ratio < src_ratio else image.width * height // image.height - src_h = height if ratio >= src_ratio else image.height * width // image.width + # Floor division collapses the scaled side to 0 for extreme aspect ratios (a 1x2000 image + # scaled to fit 512x512 gives `1 * 512 // 2000 == 0`), and `Image.resize` then fails with + # "height and width must be > 0", which names neither the input nor the side that vanished. + src_w = width if ratio < src_ratio else max(1, image.width * height // image.height) + src_h = height if ratio >= src_ratio else max(1, image.height * width // image.width) resized = image.resize((src_w, src_h), resample=PIL_INTERPOLATION[self.config.resample]) res = Image.new("RGB", (width, height)) @@ -451,8 +454,8 @@ def _resize_and_crop( ratio = width / height src_ratio = image.width / image.height - src_w = width if ratio > src_ratio else image.width * height // image.height - src_h = height if ratio <= src_ratio else image.height * width // image.width + src_w = width if ratio > src_ratio else max(1, image.width * height // image.height) + src_h = height if ratio <= src_ratio else max(1, image.height * width // image.width) resized = image.resize((src_w, src_h), resample=PIL_INTERPOLATION[self.config.resample]) res = Image.new("RGB", (width, height)) diff --git a/tests/others/test_image_processor.py b/tests/others/test_image_processor.py index 88e82ab54b82..955e43bd333a 100644 --- a/tests/others/test_image_processor.py +++ b/tests/others/test_image_processor.py @@ -308,3 +308,28 @@ def test_vae_image_processor_resize_np(self): assert out_np.shape == exp_np_shape, ( f"resized image output shape '{out_np.shape}' didn't match expected shape '{exp_np_shape}'." ) + + def test_resize_fill_and_crop_handle_extreme_aspect_ratios(self): + """A side must never be scaled down to zero pixels. + + `src_w`/`src_h` are computed with floor division, so scaling a very thin image to fit a + square target collapses the short side: `1 * 512 // 2000 == 0`. `PIL.Image.resize` then + raises `ValueError: height and width must be > 0`, which names neither the input nor the + side that vanished. + """ + image_processor = VaeImageProcessor(vae_scale_factor=1) + + for size in [(1, 2000), (2000, 1), (1, 1)]: + image = PIL.Image.new("RGB", size) + for resize_mode in ("fill", "crop"): + out = image_processor.resize(image, height=512, width=512, resize_mode=resize_mode) + assert out.size == (512, 512), f"{size} with resize_mode={resize_mode} gave {out.size}" + + def test_resize_fill_and_crop_unchanged_for_ordinary_images(self): + """The clamp must not perturb sizes that were already computed correctly.""" + image_processor = VaeImageProcessor(vae_scale_factor=1) + + for size in [(64, 64), (900, 3), (3, 900), (1024, 768)]: + image = PIL.Image.new("RGB", size) + for resize_mode in ("fill", "crop"): + assert image_processor.resize(image, height=512, width=512, resize_mode=resize_mode).size == (512, 512)