fix: pass (width, height) to Pillow in the Resize transform (#697)

* fix: pass (width, height) to Pillow in the Resize transform

`resize()` handed a tuple size straight to `PIL.Image.resize()`. fastembed
keeps sizes as (height, width) — `Transform.from_config` builds the tuple
as `(size["height"], size["width"])` — while Pillow takes (width, height),
so a non-square image processor configuration produced a transposed image:

    Resize(size=(100, 200))(Image.new("RGB", (300, 300)))[0].size
    # (100, 200), expected (200, 100)

Square sizes are unaffected, which is why this went unnoticed. The int
branch of `resize()` already emits Pillow order and is untouched, as are
`resize_ndarray()`'s callers, which pass (width, height) explicitly.

`Resize.__call__` is the only caller of this function and always supplies
fastembed's height-first order, so converting here is safe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* tests: simplify tests

---------

Co-authored-by: George Panchuk <george.panchuk@qdrant.tech>
This commit is contained in:
Ramnath0521
2026-09-21 23:07:15 +07:00
committed by GitHub
co-authored by Claude Opus 5 George Panchuk
parent a3a798f4f3
commit 5c4d9b04bd
2 changed files with 34 additions and 1 deletions
+6 -1
View File
@@ -98,7 +98,12 @@ def resize(
resample: int | Image.Resampling = Image.Resampling.BILINEAR,
) -> Image.Image:
if isinstance(size, tuple):
return image.resize(size, resample)
# fastembed keeps sizes as (height, width) — `Compose.from_config` builds the
# tuple as (size["height"], size["width"]) — while Pillow's resize takes
# (width, height). The two agree for square sizes, so this only shows up on a
# non-square image processor configuration.
height, width = size
return image.resize((width, height), resample)
height, width = image.height, image.width
short, long = (width, height) if width <= height else (height, width)
+28
View File
@@ -0,0 +1,28 @@
import pytest
from PIL import Image
from fastembed.image.transform.functional import resize
@pytest.mark.parametrize(
("size", "expected"),
[
((100, 200), (200, 100)), # the bug: a non-square size came back transposed
((224, 224), (224, 224)), # the square path every shipped model takes
],
)
def test_resize_tuple_is_height_width(size: tuple[int, int], expected: tuple[int, int]) -> None:
"""A ``(height, width)`` size must reach Pillow as ``(width, height)``."""
resized = resize(Image.new("RGB", (300, 300)), size=size)
assert resized.size == expected # PIL reports (width, height)
def test_resize_int_keeps_shortest_edge_behaviour() -> None:
"""The int branch already emitted Pillow order; it must not be disturbed."""
landscape = Image.new("RGB", (400, 200))
portrait = Image.new("RGB", (200, 400))
# size sets the shortest edge, and the aspect ratio is preserved.
assert resize(landscape, size=100).size == (200, 100)
assert resize(portrait, size=100).size == (100, 200)