2 Commits
Author SHA1 Message Date
Serhii ZghamaandGeorge Panchuk cbe60bf9dc fix(image): normalize batched (N, C, H, W) input along the channel axis (#682)
* fix(image): normalize batched input along the channel axis

normalize() advertises 4D (N, C, H, W) support via its num_channels
branch and the channel-count validation, but the actual math used
((image.T - mean) / std).T. Transpose reverses every axis, so on 4D
input the channels no longer line up with mean/std: it raises when
N != C and silently normalizes along the batch axis when N == C.
Reshape mean/std to broadcast on the real channel axis instead; the
(C, H, W) path is unchanged.

* test(image): cover channel-wise normalize for 3D and batched input

* refactor

---------

Co-authored-by: George Panchuk <george.panchuk@qdrant.tech>
2026-09-22 15:59:21 +07:00
5c4d9b04bd 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>
2026-09-21 23:07:15 +07:00