Signed-off-by: princepride <wangzhipeng628@gmail.com> Signed-off-by: 汪志鹏 <wangzhipeng628@gmail.com>
170 lines
5.6 KiB
Python
170 lines
5.6 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
"""
|
|
End-to-end test for SenseNova-U1 img2img generation.
|
|
|
|
This test validates that the SenseNova-U1 model generates edited images from
|
|
an input image and text prompt that match expected reference pixel values
|
|
within a ±10 tolerance.
|
|
|
|
Equivalent to running:
|
|
python examples/offline_inference/sensenova_u1/end2end.py \
|
|
--prompt "Turn this into an oil painting" \
|
|
--image 2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg \
|
|
--width 2560 --height 1669 \
|
|
--seed 42 --num-steps 50 \
|
|
--cfg-scale 4.0 --img-cfg-scale 1.0 --cfg-norm none \
|
|
--think --print-think
|
|
"""
|
|
|
|
import os
|
|
from typing import Any
|
|
|
|
os.environ["VLLM_WORKER_MULTIPROC_METHOD"] = "spawn"
|
|
|
|
import pytest
|
|
from PIL import Image
|
|
from vllm.assets.image import ImageAsset
|
|
|
|
from tests.helpers.mark import hardware_test
|
|
from tests.helpers.runtime import OmniRunner
|
|
from vllm_omni.entrypoints.omni import Omni
|
|
from vllm_omni.inputs.data import OmniDiffusionSamplingParams
|
|
|
|
# Reference pixel data extracted from the known-good output image generated by:
|
|
# python examples/offline_inference/sensenova_u1/end2end.py \
|
|
# --prompt "Turn this into an oil painting" \
|
|
# --image 2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg \
|
|
# --width 2560 --height 1669 --seed 42 --num-steps 50 \
|
|
# --cfg-scale 4.0 --img-cfg-scale 1.0 --cfg-norm none --think
|
|
REFERENCE_PIXELS = [
|
|
{"position": (100, 100), "rgb": (95, 131, 174)},
|
|
{"position": (400, 50), "rgb": (142, 153, 174)},
|
|
{"position": (700, 100), "rgb": (127, 149, 174)},
|
|
{"position": (150, 400), "rgb": (99, 141, 183)},
|
|
{"position": (512, 336), "rgb": (184, 177, 165)},
|
|
{"position": (700, 400), "rgb": (106, 139, 168)},
|
|
{"position": (100, 600), "rgb": (170, 172, 155)},
|
|
{"position": (400, 600), "rgb": (159, 171, 169)},
|
|
{"position": (700, 600), "rgb": (139, 172, 187)},
|
|
{"position": (256, 256), "rgb": (136, 158, 180)},
|
|
]
|
|
|
|
PIXEL_TOLERANCE = 10
|
|
|
|
DEFAULT_PROMPT = "Turn this into an oil painting"
|
|
|
|
EXPECTED_OUTPUT_SIZE = (2560, 1664)
|
|
|
|
|
|
def _load_input_image() -> Image.Image:
|
|
"""Load the test input image via vllm's ImageAsset."""
|
|
return ImageAsset("2560px-Gfp-wisconsin-madison-the-nature-boardwalk").pil_image.convert("RGB")
|
|
|
|
|
|
def _build_sampling_params() -> OmniDiffusionSamplingParams:
|
|
"""Build sampling parameters for SenseNova-U1 img2img generation."""
|
|
return OmniDiffusionSamplingParams(
|
|
height=1669,
|
|
width=2560,
|
|
seed=42,
|
|
num_inference_steps=50,
|
|
extra_args={
|
|
"cfg_scale": 4.0,
|
|
"img_cfg_scale": 1.0,
|
|
"cfg_norm": "none",
|
|
"timestep_shift": 3.0,
|
|
"cfg_interval": (0.0, 1.0),
|
|
"batch_size": 1,
|
|
"think": True,
|
|
"t_eps": 0.02,
|
|
},
|
|
)
|
|
|
|
|
|
def _extract_generated_image(omni_outputs: list) -> Image.Image | None:
|
|
"""Extract the generated image from Omni outputs (single-stage DiT)."""
|
|
for req_output in omni_outputs:
|
|
if images := getattr(req_output, "images", None):
|
|
return images[0]
|
|
return None
|
|
|
|
|
|
def _validate_pixels(
|
|
image: Image.Image,
|
|
reference_pixels: list[dict[str, Any]] = REFERENCE_PIXELS,
|
|
tolerance: int = PIXEL_TOLERANCE,
|
|
) -> None:
|
|
"""Validate that image pixels match expected reference values.
|
|
|
|
Args:
|
|
image: The PIL Image to validate.
|
|
reference_pixels: List of dicts with 'position' (x, y) and 'rgb' (R, G, B).
|
|
tolerance: Maximum allowed difference per color channel.
|
|
|
|
Raises:
|
|
AssertionError: If any pixel differs beyond tolerance.
|
|
"""
|
|
for ref in reference_pixels:
|
|
x, y = ref["position"]
|
|
expected = ref["rgb"]
|
|
actual = image.getpixel((x, y))[:3]
|
|
assert all(abs(a - e) <= tolerance for a, e in zip(actual, expected)), (
|
|
f"Pixel mismatch at ({x}, {y}): expected {expected}, got {actual}"
|
|
)
|
|
|
|
|
|
def _generate_sensenova_u1_img2img(
|
|
omni: Omni,
|
|
input_image: Image.Image,
|
|
prompt: str = DEFAULT_PROMPT,
|
|
) -> Image.Image:
|
|
"""Generate an edited image using SenseNova-U1 model with img2img pipeline.
|
|
|
|
Args:
|
|
omni: The Omni instance to use for generation.
|
|
input_image: The input PIL Image for img2img.
|
|
prompt: The text prompt for image editing.
|
|
|
|
Returns:
|
|
The generated PIL Image.
|
|
|
|
Raises:
|
|
AssertionError: If no image is generated or size is incorrect.
|
|
"""
|
|
sampling_params = _build_sampling_params()
|
|
|
|
omni_outputs = list(
|
|
omni.generate(
|
|
prompts={
|
|
"prompt": prompt,
|
|
"multi_modal_data": {"image": input_image},
|
|
"modalities": ["img2img"],
|
|
},
|
|
sampling_params_list=sampling_params,
|
|
)
|
|
)
|
|
|
|
generated_image = _extract_generated_image(omni_outputs)
|
|
assert generated_image is not None, "No images generated"
|
|
assert generated_image.size == EXPECTED_OUTPUT_SIZE, f"Expected {EXPECTED_OUTPUT_SIZE}, got {generated_image.size}"
|
|
|
|
return generated_image
|
|
|
|
|
|
@pytest.mark.core_model
|
|
@pytest.mark.advanced_model
|
|
@pytest.mark.diffusion
|
|
@hardware_test(res={"cuda": "H100"})
|
|
def test_sensenova_u1_img2img(run_level):
|
|
"""Test SenseNova-U1 img2img (single-stage diffusion, no deploy YAML)."""
|
|
input_image = _load_input_image()
|
|
with OmniRunner(
|
|
"SenseNova/SenseNova-U1-8B-MoT",
|
|
stage_configs_path=None,
|
|
) as runner:
|
|
generated_image = _generate_sensenova_u1_img2img(runner.omni, input_image)
|
|
if run_level == "advanced_model":
|
|
_validate_pixels(generated_image)
|