@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 47930b59ca
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

[Bugfix] Handle HWC images in ImageProcessorItems.get_image_size (#45057)

Signed-off-by: YellowFoxH4XOR <yellowfoxh4xor@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>

Akshat katiyar committed Jun 10, 2026 at 11:05 UTC 47930b59ca363349d81f112910f6ff8e795f089c
2 files changed +58 -1
tests/multimodal/test_parse.py new
+51
@@ -0,0 +1,51 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +import numpy as np
4 +import pytest
5 +import torch
6 +from PIL import Image
7 +
8 +from vllm.multimodal.parse import ImageProcessorItems, VideoProcessorItems
9 +
10 +H, W = 480, 640
11 +
12 +
13 +@pytest.mark.parametrize(
14 + "image",
15 + [
16 + Image.new("RGB", (W, H)),
17 + # HWC, e.g. from np.array(PIL.Image)
18 + np.zeros((H, W, 3), dtype=np.uint8),
19 + torch.zeros((H, W, 3), dtype=torch.uint8),
20 + # CHW, standard PyTorch / numpy convention
21 + np.zeros((3, H, W), dtype=np.uint8),
22 + torch.zeros((3, H, W), dtype=torch.uint8),
23 + ],
24 +)
25 +def test_image_size_hwc_chw(image):
26 + """Image sizes must be channel-layout agnostic.
27 +
28 + `get_image_size` determines the multimodal placeholder count; reading an
29 + HWC array (the layout `np.array(PIL.Image)` produces) as CHW yields a
30 + bogus size and a placeholder/embedding count mismatch at inference time.
31 + """
32 + items = ImageProcessorItems([image])
33 +
34 + assert items.get_image_size(0) == (W, H)
35 +
36 +
37 +@pytest.mark.parametrize(
38 + "frame",
39 + [
40 + Image.new("RGB", (W, H)),
41 + np.zeros((H, W, 3), dtype=np.uint8),
42 + torch.zeros((H, W, 3), dtype=torch.uint8),
43 + np.zeros((3, H, W), dtype=np.uint8),
44 + torch.zeros((3, H, W), dtype=torch.uint8),
45 + ],
46 +)
47 +def test_frame_size_hwc_chw(frame):
48 + """`get_frame_size` must stay consistent with `get_image_size`."""
49 + items = VideoProcessorItems([[frame]])
50 +
51 + assert items.get_frame_size(0) == (W, H)
vllm/multimodal/parse.py
+7 -1
@@ -334,7 +334,13 @@ class ImageProcessorItems(ProcessorBatchItems[HfImageItem | None]):
334 if isinstance(image, PILImage.Image):
335 return ImageSize(*image.size)
336 if isinstance(image, (np.ndarray, torch.Tensor)):
337 - _, h, w = image.shape
337 + if image.ndim == 3 and image.shape[-1] in (1, 3, 4):
338 + # HWC format (e.g. from np.array(PIL.Image)).
339 + # PIL images are always channels-last.
340 + h, w = image.shape[0], image.shape[1]
341 + else:
342 + # CHW format (standard PyTorch / numpy convention).
343 + _, h, w = image.shape
344 return ImageSize(w, h)
345
346 assert_never(image)