mirror of
https://github.com/qdrant/fastembed.git
synced 2026-09-24 15:07:50 -05:00
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:
co-authored by
Claude Opus 5
George Panchuk
parent
a3a798f4f3
commit
5c4d9b04bd
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user