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)