LoopToken
图片生成

Qwen Image Edit

qwen-image-edit、qwen-image-edit-plus、qwen-image-edit-max 图像编辑接口

Qwen Image Edit 系列通过参考图和编辑指令完成局部修改、文字替换、风格迁移、多图融合与构图调整。三个公开模型均使用同步 JSON 接口;能力与参数不可互换。

能力矩阵

能力qwen-image-editqwen-image-edit-plusqwen-image-edit-max
文本生成图片不支持纯文本请求不支持纯文本请求不支持纯文本请求
图生图 / 局部编辑支持支持支持,工业设计、几何与角色一致性更强
多图融合支持支持支持
最大参考图333
最大输出固定 16,默认 16,默认 1
自定义 size不支持支持支持
默认尺寸 / 比例由模型按输入推断总像素接近 1024*1024,比例接近最后一张输入图总像素接近 1024*1024,比例接近最后一张输入图
输出格式PNGPNGPNG
URL 输入支持支持支持
Base64 Data URL支持支持支持
流式响应不在稳定范围不在稳定范围不在稳定范围
Prompt 智能改写不支持支持,默认开启支持,默认开启
seed / 水印支持 / 支持支持 / 支持支持 / 支持

当前环境没有可安全使用的 API 凭据。上述模型服务能力来自后端、仓库接口 PDF 和官方资料的静态交叉核对,未通过 LoopToken 在线调用验证;超出本页明确范围的格式、尺寸或组合应视为不支持或未知。

接口

  • Endpoint:POST /v1/images/edits
  • Base URL:https://api.vibelab.me
  • Authorization: Bearer <your-api-key>
  • Content-Type: application/json

请求体使用 input.messages 放置图片和文字,使用 parameters 放置生成选项。不要发送 multipart 表单。

参数

参数类型必填默认值范围 / 格式适用性与依赖
modelstring-三个精确模型名之一所有请求
inputobject-包含 messages所有请求
input.messagesarray-恰好 1 个元素仅单轮
input.messages[0].rolestring-固定 user所有请求
input.messages[0].contentarray-1-3 个 image 和恰好 1 个 text图片顺序有意义
content[].imagestring-公网 URL 或 data:image/<mime>;base64,...每张图片一个内容项
content[].textstring-中英文;基础版/Plus 最多 800 Token,Max 最多 1300 Token仅 1 个文字项;可用“图一”“图二”指代顺序
parametersobject{}下列字段省略使用模型默认值
parameters.sizestring按输入推断宽*高仅 Plus/Max;宽、高各 512-2048,实际结果对齐到最接近的 16 倍数
parameters.ninteger1Plus/Max 为 1-6基础版的模型约束是固定 1;不要提交其他值。LoopToken 不在本地强制该限制
parameters.negative_promptstring最多 500 字符三个模型
parameters.prompt_extendbooleantruetrue / false仅 Plus/Max;开启可能增加耗时
parameters.watermarkbooleanfalsetrue / false三个模型;开启后右下角添加模型水印
parameters.seedinteger随机0-2147483647三个模型;相同值不保证完全一致

未列出的顶层字段和参数没有稳定支持承诺。LoopToken 当前主要读取 modelparameters.nparameters.size 做路由和计费,其他字段由模型服务校验;越界通常会归一为 502 upstream_error

图片输入

URL 引用

{
  "model": "qwen-image-edit-plus",
  "input": {
    "messages": [{
      "role": "user",
      "content": [
        {"image": "https://example.com/photo.jpg"},
        {"text": "将背景改为星空,保持人物不变"}
      ]
    }]
  }
}

URL 必须能被模型服务直接访问,不能依赖 Cookie、登录态、内网 DNS 或临时请求头。建议使用 HTTPS,并保证处理期间持续可访问;重定向、签名 URL 到期和防盗链均可能导致 502。

Base64 Data URL

{
  "model": "qwen-image-edit",
  "input": {
    "messages": [{
      "role": "user",
      "content": [
        {"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB..."},
        {"text": "将图片改成水彩风格"}
      ]
    }]
  }
}

必须包含 data: 前缀、准确 MIME 和 ;base64,。已记录格式为 JPEG/JPG、PNG、BMP、WEBP,单张不超过 10 MB。不要发送裸 Base64。GIF、TIFF、HEIC、SVG、带 alpha 的透明像素保留方式、最小输入尺寸、输入总像素和极端宽高比均未建立稳定范围。

多图按 content 数组顺序编号,提示词可明确写“图一”“图二”。未显式设置 size 时,Plus/Max 的输出比例接近最后一张参考图。不要依赖未描述的主体自动配对或输出顺序语义。

尺寸

qwen-image-edit 不接受自定义尺寸。Plus 和 Max 使用同一范围:宽、高各 512-2048;服务可能将结果调整为最接近的 16 倍数。

比例推荐尺寸
1:11024*10241536*1536
2:3768*11521024*1536
3:21152*7681536*1024
3:4960*12801080*1440
4:31280*9601440*1080
9:16720*12801080*1920
16:91280*7201920*1080
21:91344*5762048*872

请求示例

最小合法请求见上方 URL 示例。完整参数:

{
  "model": "qwen-image-edit-max",
  "input": {"messages": [{"role": "user", "content": [
    {"image": "https://example.com/product.png"},
    {"text": "保留产品结构,将材质改为磨砂金属,白色棚拍背景"}
  ]}]},
  "parameters": {
    "size": "1536*1024",
    "n": 2,
    "negative_prompt": "模糊,结构变形,文字乱码",
    "prompt_extend": false,
    "watermark": false,
    "seed": 20260712
  }
}

多参考图融合:

{
  "model": "qwen-image-edit-max",
  "input": {"messages": [{"role": "user", "content": [
    {"image": "https://example.com/city.jpg"},
    {"image": "https://example.com/character.png"},
    {"text": "以图一为背景,将图二角色自然放在街道中央,保持角色服装和面部特征"}
  ]}]}
}

文字局部编辑:

{
  "model": "qwen-image-edit-plus",
  "input": {"messages": [{"role": "user", "content": [
    {"image": "https://example.com/sign.png"},
    {"text": "只把招牌文字改为“LoopToken”,其余画面不变"}
  ]}]}
}

中文海报:

{
  "model": "qwen-image-edit-max",
  "input": {"messages": [{"role": "user", "content": [
    {"image": "https://example.com/poster.png"},
    {"text": "保留版式,将主标题改为“向海而歌”,日期改为“7月20日 18:00”"}
  ]}]},
  "parameters": {"size": "1080*1440", "n": 1, "prompt_extend": false}
}

群体合成:

{
  "model": "qwen-image-edit-max",
  "input": {"messages": [{"role": "user", "content": [
    {"image": "https://example.com/person-a.png"},
    {"image": "https://example.com/person-b.png"},
    {"image": "https://example.com/studio.jpg"},
    {"text": "将图一和图二的人物并排放入图三摄影棚,保持各自面部和服装特征"}
  ]}]}
}

本接口没有已确认的流式请求示例;不要发送 stream

cURL

curl --fail-with-body https://api.vibelab.me/v1/images/edits \
  -H "Authorization: Bearer $LOOPTOKEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen-image-edit-plus","input":{"messages":[{"role":"user","content":[{"image":"https://example.com/photo.jpg"},{"text":"将背景改为雪山"}]}]},"parameters":{"n":1}}'

Python

import mimetypes, os
from pathlib import Path
from urllib.parse import urlparse
import requests

url = "https://api.vibelab.me/v1/images/edits"
payload = {"model": "qwen-image-edit-plus", "input": {"messages": [{"role": "user", "content": [
    {"image": "https://example.com/photo.jpg"}, {"text": "将背景改为雪山"}
]}]}, "parameters": {"n": 2}}
r = requests.post(url, headers={"Authorization": f"Bearer {os.environ['LOOPTOKEN_API_KEY']}"}, json=payload, timeout=300)
try:
    body = r.json()
except ValueError:
    body = {"error": {"message": r.text}}
if not r.ok:
    raise RuntimeError(f"HTTP {r.status_code}: {body.get('error', body)}")
images = [item["image"] for choice in body.get("output", {}).get("choices", [])
          for item in choice.get("message", {}).get("content", []) if item.get("image")]
if not images:
    raise RuntimeError("response contained no image")
for i, image_url in enumerate(images, 1):
    download = requests.get(image_url, timeout=120); download.raise_for_status()
    mime = download.headers.get("content-type", "").split(";", 1)[0]
    ext = mimetypes.guess_extension(mime) or Path(urlparse(image_url).path).suffix or ".png"
    Path(f"qwen-edit-{i}{ext}").write_bytes(download.content)

Node.js

import { writeFile } from 'node:fs/promises';
import path from 'node:path';

const response = await fetch('https://api.vibelab.me/v1/images/edits', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.LOOPTOKEN_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ model: 'qwen-image-edit-plus', input: { messages: [{ role: 'user', content: [
    { image: 'https://example.com/photo.jpg' }, { text: '将背景改为雪山' },
  ] }] }, parameters: { n: 2 } }),
});
const text = await response.text();
let body; try { body = JSON.parse(text); } catch { body = { error: { message: text } }; }
if (!response.ok) throw new Error(`HTTP ${response.status}: ${JSON.stringify(body.error ?? body)}`);
const images = (body.output?.choices ?? []).flatMap((c) => (c.message?.content ?? []).filter((x) => x.image));
if (!images.length) throw new Error('response contained no image');
const extensions = { 'image/png': '.png', 'image/jpeg': '.jpg', 'image/webp': '.webp', 'image/bmp': '.bmp' };
for (const [i, item] of images.entries()) {
  const download = await fetch(item.image);
  if (!download.ok) throw new Error(`download failed: HTTP ${download.status}`);
  const mime = (download.headers.get('content-type') ?? '').split(';', 1)[0];
  const ext = extensions[mime] ?? (path.extname(new URL(item.image).pathname) || '.png');
  await writeFile(`qwen-edit-${i + 1}${ext}`, Buffer.from(await download.arrayBuffer()));
}

响应

{
  "output": {"choices": [{"finish_reason": "stop", "message": {"role": "assistant", "content": [
    {"image": "https://example.invalid/generated/edit-1.png?Expires=0"},
    {"image": "https://example.invalid/generated/edit-2.png?Expires=0"}
  ]}}]},
  "usage": {"image_count": 2, "width": 1536, "height": 1024}
}
字段类型说明
output.choices[]arraychoice 顺序按响应原样保留
output.choices[].finish_reasonstring正常完成通常为 stop
output.choices[].message.rolestring通常为 assistant
output.choices[].message.content[]array图片按数组顺序处理,不假设与输入图一一对应
output.choices[].message.content[].imagestringPNG 临时下载 URL
usage.image_countinteger返回图片数
usage.width / usage.heightinteger实际输出像素

Qwen Image 编辑的成功响应体由模型服务透传。usage 及其他附加字段只有在模型服务实际返回时才存在,LoopToken 不向响应体注入 sizerequest_id。LoopToken 请求 ID 位于 HTTP 响应头 X-Request-Id;排查时应同时保存该响应头。

下载链接是临时资源,应立即下载并转存。具体有效期未经在线验证,不属于稳定合同;不要依赖固定下载域名、长期可恢复性或 URL 排序语义。

错误处理

{"error":{"message":"invalid image generation request","type":"invalid_request","code":"invalid_request","request_id":"req_example"}}
HTTPerror.type / error.code含义
400invalid_requestLoopToken 无法读取请求体、JSON 无法解析,或 model 缺失、为空、不是可读取的字符串
401鉴权错误API Key 缺失或无效
402insufficient_credits余额不足
403model_not_allowedKey 的模型白名单不允许
404model_not_found未知模型名
502upstream_error模型服务返回非 2xx,例如拒绝参数、素材或内容
503no_available_channel当前没有可处理该模型的服务资源;也可能在可重试失败后返回

模型服务错误会统一转换为 HTTP 502 upstream_error,不透出其内部错误码。客户端应先检查 HTTP 状态,再读取 error.messageerror.codeerror.request_id。上述错误包络来自实现,因缺少凭据尚未在线验证。

缺少 inputinput.messages、图片或文字指令不会被 LoopToken 本地归为 400;普通 Qwen 请求会先转发。模型服务若以非 2xx 拒绝,会被归一为 HTTP 502 upstream_error

普通 Qwen 路径若收到模型服务 2xx,即使 LoopToken 无法识别其中的图片,也会原样返回 HTTP 200;该情况不会转换为 upstream_error,计费按下节所述回退到提交的 n

计费

三个模型均按图片计费。请求开始按提交的正数 parameters.n 预扣,省略、零或负数时按 1 张;此逻辑对三个模型相同。基础版固定输出 1 张是模型服务约束,不是 LoopToken 的本地预扣约束:误提交 n>1 可能先按该数预扣,模型服务拒绝并返回非 2xx 时会全额退款。成功响应按 output.choices[].message.content[].image 中识别到的图片数结算;模型服务返回 2xx 但得到 Count=0 时,结算回退到上述本地提交数量。usage 只记录模型服务用量,不代表扣费金额。尺寸价格键未命中时使用当前模型的 default 单价。最终价格以模型列表为准。

本页内容