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)