news 2026/9/17 10:26:48

vLLM-Omni 图生图(Image-to-Image)在线编辑服务部署实战:Qwen-Image-Edit 单图 / 多图 / 分层生成全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vLLM-Omni 图生图(Image-to-Image)在线编辑服务部署实战:Qwen-Image-Edit 单图 / 多图 / 分层生成全指南

vLLM-Omni 图生图(Image-to-Image)在线编辑服务部署实战:Qwen-Image-Edit 单图 / 多图 / 分层生成全指南

【免费下载链接】vllm-omniA framework for efficient model inference with omni-modality models项目地址: https://gitcode.com/GitHub_Trending/vl/vllm-omni

vLLM-Omni 基于 OpenAI 兼容的多模态 Chat Completions 协议,提供开箱即用的图生图(Image-to-Image)在线编辑服务。本指南以 Qwen-Image-Edit 为例,完整讲解服务端启动、四种客户端调用方式、请求 / 响应协议、生成参数与分层生成(Qwen-Image-Layered)、多图编辑(Qwen-Image-Edit-2509)等实战要点。阅读完成后,你将能够独立部署一个可对外提供图像编辑能力的在线服务,并熟练使用 curl、OpenAI Python SDK、命令行客户端与 Gradio 界面四种方式完成调用与结果解码。

一、功能概览与适用模型

该示例位于仓库 examples/online_serving/image_to_image,用于演示如何通过 vLLM-Omni 部署 Image-to-Image 模型并提供在线图像编辑服务。除 Qwen-Image-Edit 外,还支持 BAGEL 等其他 Image-to-Image 扩散流水线。

模型能力说明
Qwen/Qwen-Image-Edit单图文本引导编辑基础图生图编辑模型,风格迁移、调色、场景转换等
Qwen/Qwen-Image-Edit-2509多图输入编辑使用QwenImageEditPlusPipeline,支持在同一消息中传入多张图片并组合编辑
Qwen/Qwen-Image-Layered分层图像生成从参考图 + 文本提示分解出多层图像
ByteDance-Seed/BAGEL-7B-MoT图像编辑(BAGEL 流水线)通过extra_body透传cfg_text_scalecfg_img_scale等模型专属参数

从源码结构看,vLLM-Omni 将每种扩散模型封装为独立的 pipeline,例如 pipeline_qwen_image_edit.py 对应单图编辑、pipeline_qwen_image_edit_plus.py 对应多图编辑(QwenImageEditPlusPipeline,其注释明确"Edit prompt template - different from generation template, supports multiple images")。不同模型由服务启动时指定的--omni模式自动注册与调度。

二、服务端部署

2.1 基础启动(单图编辑)

vllm serve Qwen/Qwen-Image-Edit --omni --port 8092
  • --omni为 vLLM-Omni 的多模态统一入口开关;
  • --port 8092指定 OpenAI 兼容 API 端口。

显存不足(OOM)提示:若遇到显存溢出或 GPU 显存有限,可开启 VAE slicing 与 tiling 降低显存占用:

vllm serve Qwen/Qwen-Image-Edit --omni --port 8092 --vae-use-slicing --vae-use-tiling

2.2 多图编辑启动(Qwen-Image-Edit-2509)

vllm serve Qwen/Qwen-Image-Edit-2509 --omni --port 8092

多图编辑的核心区别在于请求的 user message content 中可包含多张图片(顺序有意义),详见下文"多图编辑"一节。

2.3 使用启动脚本(带参数)

仓库提供了现成启动脚本 run_server.sh:

bash run_server.sh

脚本内部支持通过环境变量覆盖模型与端口:

MODEL=Qwen/Qwen-Image-Edit-2509 bash run_server.sh # 换模型 PORT=8093 bash run_server.sh # 换端口

脚本实现如下(默认MODEL=Qwen/Qwen-Image-EditPORT=8092):

#!/bin/bash # Qwen-Image-Edit online serving startup script MODEL="${MODEL:-Qwen/Qwen-Image-Edit}" PORT="${PORT:-8092}" echo "Starting Qwen-Image-Edit server..." echo "Model: $MODEL" echo "Port: $PORT" vllm serve "$MODEL" --omni \ --port "$PORT"

服务就绪的标志是日志中出现INFO: Application startup complete.(其前为Started server process [PID]Waiting for application startup.)。若需更大并发或更低单请求延迟,可参考 Qwen/Qwen-Image-Edit.md 中验证过的--tensor-parallel-size 2(TP=2)配置。

2.4 部署其他 Image-to-Image 模型(BAGEL)

vllm serve ByteDance-Seed/BAGEL-7B-MoT --omni --port 8091

BAGEL 与 Qwen 系列在调用方式上的差异主要体现在模型专属生成参数上(如cfg_text_scalecfg_img_scale),完整参数列表见 BAGEL-7B-MoT.md。

三、客户端调用:四种方式

3.1 方法一:curl(推荐用于快速验证)

仓库封装了 curl 调用脚本 run_curl_image_edit.sh,一行完成"读图 → 编码 → 请求 → 解码 → 落盘":

bash run_curl_image_edit.sh input.png "Convert this image to watercolor style" # 可指定输出文件(默认为 image_edit_<时间戳>.png) bash run_curl_image_edit.sh input.png "Convert to cartoon style" output.png

该脚本的实现要点(值得借鉴):通过管道把 base64 数据喂给jq -Rs构造 JSON,从而避免超大图片突破 shell 的 ARG_MAX 参数长度限制;请求体结构为messages[0].content中先放文本指令、再放image_url格式的 data URL,生成参数统一放入extra_body

不借助脚本、直接使用 curl 的方式如下:

IMG_B64=$(base64 -w0 input.png) cat <<EOF > request.json { "messages": [{ "role": "user", "content": [ {"type": "text", "text": "Convert this image to watercolor style"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,$IMG_B64"}} ] }], "extra_body": { "height": 1024, "width": 1024, "num_inference_steps": 50, "guidance_scale": 1, "seed": 42 } } EOF curl -s http://localhost:8092/v1/chat/completions \ -H "Content-Type: application/json" \ -d @request.json \ | jq -r '.choices[0].message.content[0].image_url.url' \ | cut -d',' -f2 | base64 -d > output.png

注意:直接使用 curl 时,生成参数必须写在 JSON 顶层的字面量"extra_body"键内(如上所示)。

3.2 方法二:OpenAI Python SDK

import base64 from openai import OpenAI client = OpenAI(base_url="http://localhost:8092/v1", api_key="none") with open("input.png", "rb") as f: img_b64 = base64.b64encode(f.read()).decode() response = client.chat.completions.create( model="Qwen/Qwen-Image-Edit", messages=[{ "role": "user", "content": [ {"type": "text", "text": "Convert to watercolor style"}, {"type": "image_url", "image_url": { "url": f"data:image/png;base64,{img_b64}" }}, ], }], extra_body={ "num_inference_steps": 50, "guidance_scale": 1, "seed": 42, }, ) img_url = response.choices[0].message.content[0].image_url.url _, b64_data = img_url.split(",", 1) with open("output.png", "wb") as f: f.write(base64.b64decode(b64_data))

关键点:OpenAI SDK 的extra_body关键字参数会自动将生成参数合并进请求体的顶层,无需手工嵌套"extra_body"键(这与 curl 的用法相反,详见 chat_completions_api.md 中的参数处理说明)。

3.3 方法三:Python 客户端脚本

仓库提供了功能更完整的命令行客户端 openai_chat_client.py:

# 单图编辑 python openai_chat_client.py --input input.png --prompt "Convert to oil painting style" --output output.png # 多图编辑(需要 Qwen-Image-Edit-2509 服务端) python openai_chat_client.py --input input1.png input2.png --prompt "Combine these images into a single scene" --output output.png

该脚本支持的全部参数如下:

参数默认值说明
--input, -i(必填,可多个)输入图片路径,多图时按顺序传入多个
--prompt, -p(必填)编辑指令
--output, -ooutput.png输出文件
--server, -shttp://localhost:8092服务端地址
--height1024输出图像高度(像素)
--width1024输出图像宽度(像素)
--steps50去噪步数
--guidance7.5CFG 引导强度
--seed0随机种子
--negative负向提示词
--extra-body模型专属参数 JSON,如'{"cfg_text_scale": 4.0}'

模型专属参数可通过--extra-body透传,例如针对 BAGEL:

python openai_chat_client.py \ --input input.png \ --prompt "Make the scene look like a watercolor painting" \ --server http://localhost:8091 \ --extra-body '{"cfg_text_scale": 4.0, "cfg_img_scale": 1.5}'

从源码看,edit_image()会依次把heightwidthnum_inference_stepsguidance_scaleseednegative_prompt写入extra_body,并用extra_body.update()合并模型专属参数,最终以requests.post提交到/v1/chat/completions,再从响应的content[0].image_url.url中截取data:image...;base64,之后的部分解码落盘。

3.4 方法四:Gradio 交互界面

前置依赖:Gradio 属于可选依赖,需先安装[demo]extras:

pip install 'vllm-omni[demo]'

源码安装则使用pip install -e '.[demo]'

python gradio_demo.py # 浏览器访问 http://localhost:7861

gradio_demo.py 提供可视化编辑界面:支持上传主图、附加多张辅助图(对应多图编辑)、填写编辑指令与负向提示词,并提供"推理步数(10–100,默认 50)"、"CFG 强度(1.0–20.0,默认 7.5)"、"随机种子(-1 表示随机)"三个控件,内置水彩、黑白、饱和度增强、卡通、复古滤镜、昼夜转换、油画、梦幻模糊等示例指令一键填充。界面默认连接http://localhost:8092,可通过--server--port--share参数调整。

四、请求格式详解

4.1 标准 image_url 格式

{ "messages": [ { "role": "user", "content": [ {"type": "text", "text": "Convert this image to watercolor style"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}} ] } ] }

4.2 简化 image 格式

不显式指定type,直接用textimage两个键:

{ "messages": [ { "role": "user", "content": [ {"text": "Convert this image to watercolor style"}, {"image": "BASE64_IMAGE_DATA"} ] } ] }

4.3 带生成参数的请求

生成参数统一包裹在请求 JSON 的extra_body中:

{ "messages": [ { "role": "user", "content": [ {"type": "text", "text": "Convert to ink wash painting style"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}} ] } ], "extra_body": { "height": 1024, "width": 1024, "num_inference_steps": 50, "guidance_scale": 7.5, "seed": 42 } }

OpenAI SDK 用法提示:SDK 下通过extra_body关键字参数传入,SDK 会自动合并到请求体顶层:

client.chat.completions.create( model="Qwen/Qwen-Image-Edit", messages=[...], extra_body={"num_inference_steps": 50, "guidance_scale": 7.5, "seed": 42}, )

生成参数在不同客户端中的处理细节参见 Chat Completions API 指南。

五、分层图像生成(Qwen-Image-Layered)

Qwen-Image-Layered 从一张参考图 + 一段文本提示中,将图像分解为多层。启动方式:

vllm serve Qwen/Qwen-Image-Layered --omni --port 8093

5.1 curl 方式(逐层落盘)

IMG_B64=$(base64 -w0 input.png) curl -sS http://localhost:8093/v1/chat/completions \ -H "Content-Type: application/json" \ -d "$(jq -n --arg img "$IMG_B64" '{ messages: [{ role: "user", content: [ {type: "image_url", image_url: {url: ("data:image/png;base64," + $img)}}, {type: "text", text: "a rabbit"} ] }], extra_body: { num_inference_steps: 50, cfg_scale: 4.0, seed: 0, layers: 4, resolution: 640 } }')" \ | jq -r '.choices[0].message.content[] | .image_url.url | split(",")[1]' \ | while IFS= read -r b64; do ((i++)); echo "$b64" | base64 -d > "layer_${i}.png" done

5.2 OpenAI SDK 方式

import base64 from openai import OpenAI client = OpenAI(base_url="http://localhost:8093/v1", api_key="none") with open("input.png", "rb") as f: img_b64 = base64.b64encode(f.read()).decode() response = client.chat.completions.create( model="Qwen/Qwen-Image-Layered", messages=[{ "role": "user", "content": [ {"type": "image_url", "image_url": { "url": f"data:image/png;base64,{img_b64}" }}, {"type": "text", "text": "a rabbit"}, ], }], extra_body={ "num_inference_steps": 50, "cfg_scale": 4.0, "seed": 0, "layers": 4, "resolution": 640, }, ) for i, item in enumerate(response.choices[0].message.content): _, b64_data = item.image_url.url.split(",", 1) with open(f"layer_{i}.png", "wb") as f: f.write(base64.b64decode(b64_data))

5.3 Python requests 方式

import base64 import requests with open("input.png", "rb") as f: img_b64 = base64.b64encode(f.read()).decode() payload = { "messages": [{ "role": "user", "content": [ {"type": "image_url", "image_url": { "url": f"data:image/png;base64,{img_b64}" }}, {"type": "text", "text": "a rabbit"}, ], }], "extra_body": { "num_inference_steps": 50, "cfg_scale": 4.0, "seed": 0, "layers": 4, "resolution": 640, }, } resp = requests.post( "http://localhost:8093/v1/chat/completions", json=payload, timeout=600, ) data = resp.json() for i, item in enumerate(data["choices"][0]["message"]["content"]): _, b64_data = item["image_url"]["url"].split(",", 1) with open(f"layer_{i}.png", "wb") as f: f.write(base64.b64decode(b64_data))

响应特点:分层生成的响应中,choices[0].message.content是一个数组,每个元素对应一层生成图像,逐个解码即可得到所有图层。

5.4 Qwen-Image-Layered 参数表

参数类型默认值说明
layersint4分解的层数
resolutionint640维度计算分辨率(640 或 1024)
cfg_scalefloat4.0无分类器引导强度(true_cfg_scale的别名)
num_inference_stepsint50去噪步数
seedintNone随机种子,用于结果复现

六、多图编辑(Qwen-Image-Edit-2509)

多图编辑需要在content中按顺序提供多张图片(顺序有意义,模型按传入顺序理解各图角色):

{ "messages": [ { "role": "user", "content": [ {"type": "text", "text": "Combine these images into a single scene"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."} }, {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."} } ] } ] }

对应的多图命令行调用:

python openai_chat_client.py --input input1.png input2.png --prompt "Combine these images into a single scene" --output output.png

从源码看,pipeline_qwen_image_edit_plus.py 中的_get_qwen_prompt_embeds专门实现了"Build image prompt template for multiple images",即多图编辑的提示词模板拼接逻辑,这也是 2509 版与基础版在底层实现上的关键差异。

七、生成参数总览

使用/v1/chat/completions时,这些参数放在extra_body中(curl 为 JSON 字面量键,SDK 为extra_body关键字参数);使用专用的/v1/images/edits端点时,支持的生成控制项作为顶层表单字段直接传入。对于图像尺寸与数量,应使用sizen,而不是heightwidthnum_outputs_per_prompt

参数类型默认值说明
heightintNone输出图像高度(像素)
widthintNone输出图像宽度(像素)
sizestrNone输出图像尺寸(如"1024x1024"
num_inference_stepsint50去噪步数
guidance_scalefloat1.0CFG 引导强度
seedintNone随机种子(复现用)
negative_promptstrNone负向提示词
num_outputs_per_promptint1生成的图像数量
layersint4层数(Qwen-Image-Layered)
resolutionint640分辨率,640 或 1024(Qwen-Image-Layered)

参数透传机制:从 image_edit_api.md 的参数处理说明可知,vLLM-Omni 将参数直接透传给扩散流水线,不做模型级转换——未指定时使用模型自身默认值;API 层仅做基础类型与取值范围校验,模型不支持的参数可能被静默忽略,不兼容的值会由底层流水线报错。因此最佳实践是:先采用模型官方推荐参数,再按需微调。

模型专属参数示例:BAGEL 等模型可通过extra_body接收额外参数(如cfg_text_scalecfg_img_scalecfg_intervalcfg_renorm_typecfg_renorm_mintimestep_shift等),完整列表参见 BAGEL-7B-MoT.md。

八、响应格式

编辑请求的响应遵循 OpenAI Chat Completions 结构,图像以 data URL 形式内嵌在choices[0].message.content[0].image_url.url

{ "id": "chatcmpl-xxx", "created": 1234567890, "model": "Qwen/Qwen-Image-Edit", "choices": [{ "index": 0, "message": { "role": "assistant", "content": [{ "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }] }, "finish_reason": "stop" }], "usage": {...} }

解码方式为:取url字符串,按,分割后对第二部分做base64 -d(curl 场景)或base64.b64decode(Python 场景),即可得到 PNG 字节流。

若希望以 OpenAI DALL-E 兼容的/v1/images/edits端点调用(multipart/form-data表单,支持image/url输入、sizenoutput_formatoutput_compressionnegative_promptnum_inference_stepsguidance_scaletrue_cfg_scaleseedreference_imagemask_image等参数,以及 HunyuanImage3 多阶段流水线的stream=true流式返回),可参考 Image Edit API 文档。

九、常见编辑指令示例

指令说明
Convert this image to watercolor style风格迁移(水彩)
Convert the image to black and white去饱和(黑白)
Enhance the color saturation颜色调整(饱和度增强)
Convert to cartoon style卡通化
Add vintage filter effect滤镜效果(复古)
Convert daytime scene to nighttime场景转换(昼夜)

十、配套文件说明与源码索引

文件说明路径
run_server.sh服务启动脚本(支持MODEL/PORT环境变量)run_server.sh
run_curl_image_edit.shcurl 图生图调用示例(支持指定输出文件与SERVER环境变量)run_curl_image_edit.sh
openai_chat_client.py命令行 Python 客户端(支持单图 / 多图 /--extra-bodyopenai_chat_client.py
gradio_demo.pyGradio 交互界面(需[demo]extras)gradio_demo.py

延伸阅读

  • 完整请求 / 响应模式与extra_body处理: chat_completions_api.md
  • DALL-E 兼容的专用编辑端点/v1/images/edits: image_edit_api.md
  • Qwen-Image-Edit 在 H200 上的 TP=1 / TP=2 验证数据(冷/热延迟、峰值显存、分阶段耗时剖析): recipes/Qwen/Qwen-Image-Edit.md
  • BAGEL 模型专属参数完整清单: recipes/Bagel/BAGEL-7B-MoT.md
  • 底层扩散流水线实现: pipeline_qwen_image_edit.py、pipeline_qwen_image_edit_plus.py

【免费下载链接】vllm-omniA framework for efficient model inference with omni-modality models项目地址: https://gitcode.com/GitHub_Trending/vl/vllm-omni

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 10:23:31

Word+MathType论文公式居中编号右对齐:制表位与自动编号实战

论文写到最后&#xff0c;格式往往比内容还折磨人。导师一句“公式居中、编号右对齐”&#xff0c;能让不少人卡上一整天。我这些年帮人调过不少论文模板&#xff0c;本科毕设、硕士论文、期刊投稿都有&#xff0c;用 Word 配合 MathType 来做公式居中编号右对齐&#xff0c;是…

作者头像 李华
网站建设 2026/9/17 10:22:32

SQL UNION联合查询详解:纵向合并结果集的核心用法与性能优化

SQL入门系列讲到这里&#xff0c;单表查询和连接查询的基本功就算打完了。这一讲我们聊UNION联合查询——一个我工作中用得非常频繁、但很多人一开始都会搞混的集合操作。它的使用场景特别直白&#xff1a;你手上有好几张结构相同的表&#xff0c;按年份拆的、按部门拆的、按区…

作者头像 李华
网站建设 2026/9/17 10:16:19

独立版霸屏系统源码部署与二次开发实战要点解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华