Files
vllm-project--vllm/tests/multimodal/test_image.py
T
wehub-resource-sync 7ce4c8e27e
pre-commit / pre-run-check (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:55:37 +08:00

126 lines
3.9 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import struct
from io import BytesIO
from pathlib import Path
import numpy as np
import pytest
from PIL import Image, ImageChops
from vllm.multimodal.image import (
_has_transparency,
convert_image_mode,
normalize_image,
)
pytestmark = pytest.mark.cpu_test
ASSETS_DIR = Path(__file__).parent / "assets"
assert ASSETS_DIR.exists()
def test_rgb_to_rgb():
# Start with an RGB image.
original_image = Image.open(ASSETS_DIR / "image1.png").convert("RGB")
converted_image = convert_image_mode(original_image, "RGB")
# RGB to RGB should be a no-op.
diff = ImageChops.difference(original_image, converted_image)
assert diff.getbbox() is None
def test_rgba_to_rgb():
original_image = Image.open(ASSETS_DIR / "rgba.png")
original_image_numpy = np.array(original_image)
converted_image = convert_image_mode(original_image, "RGB")
converted_image_numpy = np.array(converted_image)
for i in range(original_image_numpy.shape[0]):
for j in range(original_image_numpy.shape[1]):
# Verify that all transparent pixels are converted to white.
if original_image_numpy[i][j][3] == 0:
assert converted_image_numpy[i][j][0] == 255
assert converted_image_numpy[i][j][1] == 255
assert converted_image_numpy[i][j][2] == 255
def test_palette_with_trns_to_rgb():
"""P-mode PNG with tRNS: transparent index should become white."""
# Built synthetically because the existing assets are RGBA PNGs;
# this vulnerability only affects P/L/RGB images carrying a tRNS
# chunk, which uses a different transparency mechanism.
img = Image.new("P", (4, 4))
palette = [0] * 768
palette[0:3] = [255, 0, 0]
palette[3:6] = [0, 0, 255]
img.putpalette(palette)
img.putpixel((0, 0), 0)
img.putpixel((1, 0), 1)
img.info["transparency"] = 1
assert _has_transparency(img)
converted = convert_image_mode(img, "RGB")
assert converted.mode == "RGB"
r, g, b = converted.getpixel((1, 0))
assert (r, g, b) == (255, 255, 255)
r, g, b = converted.getpixel((0, 0))
assert (r, g, b) == (255, 0, 0)
def test_l_mode_no_trns_to_rgb():
"""L-mode without transparency should convert directly."""
img = Image.new("L", (4, 4), 128)
assert not _has_transparency(img)
converted = convert_image_mode(img, "RGB")
assert converted.mode == "RGB"
assert converted.getpixel((0, 0)) == (128, 128, 128)
def test_exif_transpose_normalizes_orientation():
"""Image with EXIF orientation 3 (180-degree rotation) should be
normalized so pixel data matches visual display."""
# Built synthetically because the existing assets are PNGs which
# don't carry EXIF orientation metadata; we need a JPEG with an
# injected EXIF orientation=3 tag.
img = Image.new("RGB", (2, 1))
img.putpixel((0, 0), (255, 0, 0))
img.putpixel((1, 0), (0, 0, 255))
buf = BytesIO()
img.save(buf, format="JPEG")
jpeg_bytes = buf.getvalue()
exif_orientation_3 = (
b"\xff\xe1"
+ struct.pack(">H", 26)
+ b"Exif\x00\x00"
+ b"MM"
+ b"\x00\x2a"
+ b"\x00\x00\x00\x08"
+ b"\x00\x01"
+ b"\x01\x12"
+ b"\x00\x03"
+ b"\x00\x00\x00\x01"
+ b"\x00\x03"
+ b"\x00\x00"
+ b"\x00\x00\x00\x00"
)
soi = jpeg_bytes[:2]
rest = jpeg_bytes[2:]
patched = soi + exif_orientation_3 + rest
rotated = Image.open(BytesIO(patched))
normalized = normalize_image(rotated)
assert normalized.size == (2, 1)
def test_normalize_image_no_exif():
"""Images without EXIF should pass through unchanged."""
img = Image.new("RGB", (4, 4), (100, 100, 100))
result = normalize_image(img)
assert result.size == img.size
assert result.mode == img.mode