Alibaba Cloud · Qwen Team
Qwen-Image-Edit
The editing counterpart to Qwen-Image. It takes an existing image plus a written instruction and applies the change while leaving the rest of the frame — including identity and layout — intact.
Overview
Editing is expressed as an instruction rather than a mask. The model inherits Qwen-Image's text rendering, which is what makes editing text already present in a photograph practical.
Edit types
Semantic edits
Change what is in the scene — add, remove or replace an object — while lighting and perspective stay consistent with the original.
Appearance edits
Restyle or recolour without moving anything, so composition and structure survive the edit unchanged.
Text editing
Replace words rendered inside the image while matching the existing font, perspective and surface — the capability inherited from Qwen-Image.
Identity preservation
Faces and distinctive objects are held stable across the edit, which is what makes chained edits usable.
Hosted variants
| Endpoint | Notes |
|---|---|
wavespeed-ai/qwen-image/edit | Base instruction-driven editing. |
wavespeed-ai/qwen-image/edit-plus | Updated editing checkpoint. |
wavespeed-ai/qwen-image/edit-plus-lora | Editing with LoRA adapters applied. |
Run it
Pass a publicly reachable image URL, or upload first via POST /media/upload/binary and use the returned URL.
# 1. submit the job
curl -X POST "https://api.wavespeed.ai/api/v3/wavespeed-ai/qwen-image/edit" \
-H "Authorization: Bearer $WAVESPEED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "https://example.com/input.jpg",
"prompt": "Replace the text on the sign with \"Open until 9pm\", keep the original font and perspective",
"enable_sync_mode": false
}'
# -> {"code": 200, "data": {"id": "<request-id>", "status": "created", ...}}
# 2. poll until status is "completed"
curl "https://api.wavespeed.ai/api/v3/predictions/<request-id>/result" \
-H "Authorization: Bearer $WAVESPEED_API_KEY"
# -> {"code": 200, "data": {"status": "completed", "outputs": ["https://..."]}}
import os, time, requests
API = "https://api.wavespeed.ai/api/v3"
KEY = os.environ["WAVESPEED_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}
# submit
res = requests.post(
f"{API}/wavespeed-ai/qwen-image/edit",
headers={**HEADERS, "Content-Type": "application/json"},
json={
"image": "https://example.com/input.jpg",
"prompt": "Replace the text on the sign with \"Open until 9pm\", keep the original font and perspective",
"enable_sync_mode": false
},
timeout=30,
)
res.raise_for_status()
request_id = res.json()["data"]["id"]
# poll
while True:
data = requests.get(
f"{API}/predictions/{request_id}/result",
headers=HEADERS,
timeout=30,
).json()["data"]
if data["status"] == "completed":
print(data["outputs"][0])
break
if data["status"] == "failed":
raise RuntimeError(data.get("error", "generation failed"))
time.sleep(1.5)
const API = "https://api.wavespeed.ai/api/v3";
const KEY = process.env.WAVESPEED_API_KEY;
const headers = { Authorization: `Bearer ${KEY}` };
// submit
const submit = await fetch(`${API}/wavespeed-ai/qwen-image/edit`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
"image": "https://example.com/input.jpg",
"prompt": "Replace the text on the sign with \"Open until 9pm\", keep the original font and perspective",
"enable_sync_mode": false
}),
});
const { data: { id } } = await submit.json();
// poll
for (;;) {
const res = await fetch(`${API}/predictions/${id}/result`, { headers });
const { data } = await res.json();
if (data.status === "completed") {
console.log(data.outputs[0]);
break;
}
if (data.status === "failed") throw new Error(data.error ?? "generation failed");
await new Promise((r) => setTimeout(r, 1500));
}
Requests are asynchronous: POST returns a request id, then you poll /predictions/<id>/result until status is completed. Set enable_sync_mode: true to have the call block and return outputs directly.
API keys are created in the WaveSpeed dashboard.
Running locally
Weights are Apache-2.0 and load through diffusers.
import torch
from PIL import Image
from diffusers import QwenImageEditPipeline
pipe = QwenImageEditPipeline.from_pretrained(
"Qwen/Qwen-Image-Edit",
torch_dtype=torch.bfloat16,
).to("cuda")
image = Image.open("input.jpg").convert("RGB")
out = pipe(
image=image,
prompt='Replace the text on the sign with "Open until 9pm", '
"keep the original font and perspective",
negative_prompt=" ",
num_inference_steps=50,
true_cfg_scale=4.0,
generator=torch.Generator(device="cuda").manual_seed(42),
).images[0]
out.save("edited.png")