change3 success

This commit is contained in:
Your Name
2026-03-12 03:37:27 +00:00
commit 49c0ac8d2a
4 changed files with 1117 additions and 0 deletions
+494
View File
@@ -0,0 +1,494 @@
{
"1": {
"inputs": {
"samples": [
"22",
0
],
"vae": [
"13",
0
]
},
"class_type": "VAEDecode",
"_meta": {
"title": "VAE解码"
}
},
"2": {
"inputs": {
"to_ref": true,
"ref_main_image": false,
"ref_longest_edge": [
"20",
0
],
"ref_crop": "center",
"ref_upscale": "lanczos",
"to_vl": true,
"vl_resize": true,
"vl_target_size": 384,
"vl_crop": "center",
"vl_upscale": "bicubic",
"image": [
"10",
0
],
"configs": [
"8",
0
]
},
"class_type": "QwenEditConfigPreparer",
"_meta": {
"title": "Qwen Edit Config Preparer"
}
},
"3": {
"inputs": {
"max_size": [
"25",
0
],
"image": [
"4",
0
]
},
"class_type": "QwenEditAdaptiveLongestEdge",
"_meta": {
"title": "Qwen Edit Adaptive Longest Edge"
}
},
"4": {
"inputs": {
"image": "82aaadb9-1c86-4521-86f9-3583184f6e0f.jpg"
},
"class_type": "LoadImage",
"_meta": {
"title": "加载图像"
}
},
"5": {
"inputs": {
"to_ref": true,
"ref_main_image": false,
"ref_longest_edge": [
"3",
0
],
"ref_crop": "center",
"ref_upscale": "lanczos",
"to_vl": true,
"vl_resize": true,
"vl_target_size": 384,
"vl_crop": "center",
"vl_upscale": "bicubic",
"image": [
"4",
0
],
"configs": [
"2",
0
]
},
"class_type": "QwenEditConfigPreparer",
"_meta": {
"title": "Qwen Edit Config Preparer"
}
},
"6": {
"inputs": {
"conditioning": [
"15",
0
]
},
"class_type": "ConditioningZeroOut",
"_meta": {
"title": "条件零化"
}
},
"7": {
"inputs": {
"custom_output": [
"15",
2
]
},
"class_type": "QwenEditOutputExtractor",
"_meta": {
"title": "Qwen Edit Output Extractor"
}
},
"8": {
"inputs": {
"to_ref": true,
"ref_main_image": true,
"ref_longest_edge": [
"9",
0
],
"ref_crop": "pad",
"ref_upscale": "lanczos",
"to_vl": true,
"vl_resize": true,
"vl_target_size": 384,
"vl_crop": "center",
"vl_upscale": "bicubic",
"image": [
"11",
0
]
},
"class_type": "QwenEditConfigPreparer",
"_meta": {
"title": "Qwen Edit Config Preparer"
}
},
"9": {
"inputs": {
"max_size": [
"25",
0
],
"image": [
"11",
0
]
},
"class_type": "QwenEditAdaptiveLongestEdge",
"_meta": {
"title": "Qwen Edit Adaptive Longest Edge"
}
},
"10": {
"inputs": {
"image": "5d22eef9-c44c-437c-ab8b-a19c9137a5a1.webp"
},
"class_type": "LoadImage",
"_meta": {
"title": "加载图像"
}
},
"11": {
"inputs": {
"image": "cq5dam.web.hE7E3DA.1800.1800 - 2024-04-29T161212.795.jpg"
},
"class_type": "LoadImage",
"_meta": {
"title": "加载图像"
}
},
"13": {
"inputs": {
"vae_name": "qwen_image_vae.safetensors"
},
"class_type": "VAELoader",
"_meta": {
"title": "加载VAE"
}
},
"14": {
"inputs": {
"prompt": "",
"return_full_refs_cond": true,
"instruction": "Describe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.",
"clip": [
"18",
0
],
"vae": [
"13",
0
],
"configs": [
"5",
0
]
},
"class_type": "TextEncodeQwenImageEditPlusCustom_lrzjason",
"_meta": {
"title": "TextEncodeQwenImageEditPlusCustom lrzjason"
}
},
"15": {
"inputs": {
"prompt": [
"31",
0
],
"return_full_refs_cond": true,
"instruction": "Describe the key features of the input image (color, shape, size, texture, objects, background), then explain how the user's text instruction should alter or modify the image. Generate a new image that meets the user's requirements while maintaining consistency with the original input where appropriate.",
"clip": [
"18",
0
],
"vae": [
"13",
0
],
"configs": [
"5",
0
]
},
"class_type": "TextEncodeQwenImageEditPlusCustom_lrzjason",
"_meta": {
"title": "TextEncodeQwenImageEditPlusCustom lrzjason"
}
},
"16": {
"inputs": {
"gguf_name": "FireRed-Image-Edit-1.1-transformer-q4_k_m.gguf"
},
"class_type": "LoaderGGUF",
"_meta": {
"title": "GGUF Loader"
}
},
"17": {
"inputs": {
"lora_name": "FireRed-Image-Edit-1.0-Lightning-8steps-v1.0.safetensors",
"strength_model": 1,
"model": [
"16",
0
]
},
"class_type": "LoraLoaderModelOnly",
"_meta": {
"title": "LoRA加载器(仅模型)"
}
},
"18": {
"inputs": {
"clip_name": "qwen_2.5_vl_7b_fp8_scaled.safetensors",
"type": "qwen_image",
"device": "default"
},
"class_type": "ClipLoaderGGUF",
"_meta": {
"title": "GGUF CLIP Loader"
}
},
"19": {
"inputs": {
"image": [
"1",
0
],
"pad_info": [
"7",
0
]
},
"class_type": "CropWithPadInfo",
"_meta": {
"title": "Crop With Pad Info"
}
},
"20": {
"inputs": {
"max_size": [
"25",
0
],
"image": [
"10",
0
]
},
"class_type": "QwenEditAdaptiveLongestEdge",
"_meta": {
"title": "Qwen Edit Adaptive Longest Edge"
}
},
"21": {
"inputs": {
"samples1": [
"15",
1
],
"samples2": [
"14",
1
]
},
"class_type": "LatentAdd",
"_meta": {
"title": "Latent相加"
}
},
"22": {
"inputs": {
"seed": 42692485840454,
"steps": 8,
"cfg": 1,
"sampler_name": "euler",
"scheduler": "beta",
"denoise": 1,
"model": [
"17",
0
],
"positive": [
"15",
0
],
"negative": [
"14",
0
],
"latent_image": [
"28",
0
]
},
"class_type": "KSampler",
"_meta": {
"title": "K采样器"
}
},
"24": {
"inputs": {
"upscale_method": "lanczos",
"scale_by": [
"19",
1
],
"image": [
"19",
0
]
},
"class_type": "ImageScaleBy",
"_meta": {
"title": "缩放图像(比例)"
}
},
"25": {
"inputs": {
"Number": "1536"
},
"class_type": "Int",
"_meta": {
"title": "Int"
}
},
"28": {
"inputs": {
"upscale_method": "nearest-exact",
"width": [
"29",
0
],
"height": [
"30",
0
],
"crop": "disabled",
"samples": [
"21",
0
]
},
"class_type": "LatentUpscale",
"_meta": {
"title": "缩放Latent"
}
},
"29": {
"inputs": {
"Number": "768"
},
"class_type": "Int",
"_meta": {
"title": "宽度"
}
},
"30": {
"inputs": {
"Number": "1024"
},
"class_type": "Int",
"_meta": {
"title": "高度"
}
},
"31": {
"inputs": {
"value": "图1的背景改成纯色深灰色,生成人物全身站立图,穿图2的衣服,穿图3的衣服。维持衣服材质。"
},
"class_type": "PrimitiveStringMultiline",
"_meta": {
"title": "字符串(多行)"
}
},
"32": {
"inputs": {
"upscale_method": "nearest-exact",
"width": [
"29",
0
],
"height": [
"30",
0
],
"crop": "center",
"image": [
"24",
0
]
},
"class_type": "ImageScale",
"_meta": {
"title": "缩放图像"
}
},
"33": {
"inputs": {
"filename_prefix": "ComfyUI",
"images": [
"32",
0
]
},
"class_type": "SaveImage",
"_meta": {
"title": "保存图像"
}
},
"36": {
"inputs": {
"rgthree_comparer": {
"images": [
{
"name": "A",
"selected": true,
"url": "/api/view?filename=rgthree.compare._temp_mpazx_00077_.png&type=temp&subfolder=&rand=0.7490888740442448"
},
{
"name": "B",
"selected": true,
"url": "/api/view?filename=rgthree.compare._temp_mpazx_00078_.png&type=temp&subfolder=&rand=0.37548041933644327"
}
]
},
"image_a": [
"32",
0
],
"image_b": [
"11",
0
]
},
"class_type": "Image Comparer (rgthree)",
"_meta": {
"title": "Image Comparer (rgthree)"
}
}
}
+203
View File
@@ -0,0 +1,203 @@
#!/usr/bin/env python3
"""
ComfyUI 换装服务
- 接受3个base64图片(模特、上衣、裤子)
- 上传到 ComfyUI,运行工作流
- 返回生成结果 base64 图片
"""
import asyncio
import base64
import io
import json
import time
import uuid
from pathlib import Path
import httpx
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
COMFYUI_URL = "http://127.0.0.1:8188"
# COMFYUI_URL = "http://117.50.80.187:47697"
WORKFLOW_PATH = Path(__file__).parent / "change2_2_0308.json"
# 节点 ID 映射
NODE_MODEL = "11" # 模特
NODE_SHIRT = "10" # 上衣
NODE_PANTS = "4" # 裤子
NODE_OUTPUT = "33" # 输出
app = FastAPI(title="ComfyUI 换装服务")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
class TryOnRequest(BaseModel):
model_image: str # base64 模特图片
shirt_image: str # base64 上衣图片
pants_image: str # base64 裤子图片
class TryOnResponse(BaseModel):
result_image: str # base64 结果图片
def decode_base64_image(b64_str: str) -> bytes:
"""解码 base64 图片,自动处理 data:image/... 前缀"""
if "," in b64_str:
b64_str = b64_str.split(",", 1)[1]
return base64.b64decode(b64_str)
async def upload_image(client: httpx.AsyncClient, image_bytes: bytes, filename: str) -> str:
"""上传图片到 ComfyUI,返回服务器文件名"""
files = {
"image": (filename, io.BytesIO(image_bytes), "image/jpeg"),
}
data = {"overwrite": "true"}
resp = await client.post(f"{COMFYUI_URL}/upload/image", files=files, data=data)
resp.raise_for_status()
result = resp.json()
return result["name"]
async def queue_prompt(client: httpx.AsyncClient, workflow: dict) -> str:
"""提交工作流到队列,返回 prompt_id"""
payload = {"prompt": workflow, "client_id": str(uuid.uuid4())}
resp = await client.post(f"{COMFYUI_URL}/prompt", json=payload)
resp.raise_for_status()
return resp.json()["prompt_id"]
async def wait_for_result(client: httpx.AsyncClient, prompt_id: str, timeout: int = 300) -> dict:
"""轮询历史记录,等待任务完成,返回输出节点数据"""
deadline = time.time() + timeout
while time.time() < deadline:
resp = await client.get(f"{COMFYUI_URL}/history/{prompt_id}")
resp.raise_for_status()
history = resp.json()
if prompt_id in history:
entry = history[prompt_id]
status = entry.get("status", {})
if status.get("completed"):
return entry.get("outputs", {})
if status.get("status_str") == "error":
messages = status.get("messages", [])
raise HTTPException(status_code=500, detail=f"ComfyUI 工作流执行出错: {messages}")
await asyncio.sleep(2)
raise HTTPException(status_code=504, detail="等待 ComfyUI 超时(300秒)")
async def fetch_image_as_base64(client: httpx.AsyncClient, filename: str, subfolder: str = "", type_: str = "output") -> str:
"""从 ComfyUI 下载图片并编码为 base64"""
params = {"filename": filename, "subfolder": subfolder, "type": type_}
resp = await client.get(f"{COMFYUI_URL}/view", params=params)
resp.raise_for_status()
content_type = resp.headers.get("content-type", "image/png")
b64 = base64.b64encode(resp.content).decode()
return f"data:{content_type};base64,{b64}"
@app.post("/try-on", response_model=TryOnResponse)
async def try_on(req: TryOnRequest):
"""
换装接口
- model_image: 模特图片 base64
- shirt_image: 上衣图片 base64
- pants_image: 裤子图片 base64
返回 result_image: 换装结果 base64
"""
# 加载工作流模板
workflow = json.loads(WORKFLOW_PATH.read_text(encoding="utf-8"))
async with httpx.AsyncClient(timeout=60.0) as client:
# 解码三张图片
try:
model_bytes = decode_base64_image(req.model_image)
shirt_bytes = decode_base64_image(req.shirt_image)
pants_bytes = decode_base64_image(req.pants_image)
except Exception as e:
raise HTTPException(status_code=400, detail=f"图片解码失败: {e}")
# 生成唯一文件名,避免缓存冲突
uid = uuid.uuid4().hex[:8]
model_fname = f"model_{uid}.jpg"
shirt_fname = f"shirt_{uid}.jpg"
pants_fname = f"pants_{uid}.jpg"
# 并发上传三张图片
try:
model_name, shirt_name, pants_name = await asyncio.gather(
upload_image(client, model_bytes, model_fname),
upload_image(client, shirt_bytes, shirt_fname),
upload_image(client, pants_bytes, pants_fname),
)
except httpx.HTTPError as e:
raise HTTPException(status_code=502, detail=f"上传图片到 ComfyUI 失败: {e}")
# 修改工作流节点的图片输入
workflow[NODE_MODEL]["inputs"]["image"] = model_name
workflow[NODE_SHIRT]["inputs"]["image"] = shirt_name
workflow[NODE_PANTS]["inputs"]["image"] = pants_name
# 提交工作流
try:
prompt_id = await queue_prompt(client, workflow)
except httpx.HTTPError as e:
raise HTTPException(status_code=502, detail=f"提交工作流失败: {e}")
# 等待结果(最多5分钟)
async with httpx.AsyncClient(timeout=30.0) as poll_client:
outputs = await wait_for_result(poll_client, prompt_id, timeout=300)
# 从节点33的输出获取图片
node_output = outputs.get(NODE_OUTPUT)
if not node_output:
raise HTTPException(status_code=500, detail=f"工作流未返回节点 {NODE_OUTPUT} 的输出")
images = node_output.get("images", [])
if not images:
raise HTTPException(status_code=500, detail="输出节点没有图片")
img_info = images[0]
filename = img_info["filename"]
subfolder = img_info.get("subfolder", "")
type_ = img_info.get("type", "output")
# 下载结果图片
async with httpx.AsyncClient(timeout=30.0) as dl_client:
result_b64 = await fetch_image_as_base64(dl_client, filename, subfolder, type_)
return TryOnResponse(result_image=result_b64)
@app.get("/health")
async def health():
"""健康检查"""
return {"status": "ok", "comfyui": COMFYUI_URL}
# 挂载静态文件(前端页面)
static_dir = Path(__file__).parent / "static"
static_dir.mkdir(exist_ok=True)
print(f"静态文件目录: {static_dir}")
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
@app.get("/")
async def index():
return FileResponse(str(static_dir / "index.html"))
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=47698, log_level="info")
+343
View File
@@ -0,0 +1,343 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AI 换装系统</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
background: #0f0f13;
color: #e0e0e0;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 32px 16px 64px;
}
h1 {
font-size: 2rem;
font-weight: 700;
background: linear-gradient(135deg, #a78bfa, #60a5fa, #34d399);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 8px;
text-align: center;
}
.subtitle {
font-size: 0.9rem;
color: #888;
margin-bottom: 40px;
text-align: center;
}
.upload-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
width: 100%;
max-width: 900px;
margin-bottom: 32px;
}
.upload-card {
background: #1a1a24;
border: 2px dashed #333;
border-radius: 16px;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
cursor: pointer;
transition: border-color 0.2s, background 0.2s;
position: relative;
min-height: 280px;
}
.upload-card:hover {
border-color: #7c3aed;
background: #1e1e2e;
}
.upload-card.has-image {
border-style: solid;
border-color: #6d28d9;
}
.upload-card input[type="file"] {
position: absolute;
inset: 0;
opacity: 0;
cursor: pointer;
width: 100%;
height: 100%;
}
.upload-icon {
font-size: 2.5rem;
line-height: 1;
}
.upload-label {
font-size: 1rem;
font-weight: 600;
color: #c4b5fd;
}
.upload-hint {
font-size: 0.78rem;
color: #666;
text-align: center;
}
.preview-img {
width: 100%;
max-height: 200px;
object-fit: contain;
border-radius: 10px;
display: none;
}
.preview-img.visible {
display: block;
}
.btn-run {
padding: 14px 56px;
font-size: 1.05rem;
font-weight: 700;
border: none;
border-radius: 50px;
background: linear-gradient(135deg, #7c3aed, #2563eb);
color: #fff;
cursor: pointer;
transition: opacity 0.2s, transform 0.1s;
letter-spacing: 0.5px;
}
.btn-run:hover:not(:disabled) {
opacity: 0.9;
transform: translateY(-1px);
}
.btn-run:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.status-bar {
margin-top: 20px;
font-size: 0.9rem;
color: #888;
min-height: 24px;
text-align: center;
}
.status-bar.running { color: #60a5fa; }
.status-bar.success { color: #34d399; }
.status-bar.error { color: #f87171; }
.spinner {
display: inline-block;
width: 14px;
height: 14px;
border: 2px solid #60a5fa44;
border-top-color: #60a5fa;
border-radius: 50%;
animation: spin 0.8s linear infinite;
vertical-align: middle;
margin-right: 6px;
}
@keyframes spin { to { transform: rotate(360deg); } }
.result-section {
margin-top: 40px;
width: 100%;
max-width: 500px;
display: none;
flex-direction: column;
align-items: center;
gap: 16px;
}
.result-section.visible { display: flex; }
.result-title {
font-size: 1.1rem;
font-weight: 600;
color: #a78bfa;
}
.result-img {
width: 100%;
border-radius: 16px;
box-shadow: 0 0 40px #7c3aed44;
}
.btn-download {
padding: 10px 36px;
border: 2px solid #7c3aed;
border-radius: 50px;
background: transparent;
color: #a78bfa;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.btn-download:hover {
background: #7c3aed22;
}
@media (max-width: 640px) {
.upload-grid { grid-template-columns: 1fr; }
h1 { font-size: 1.5rem; }
}
</style>
</head>
<body>
<h1>AI 换装系统</h1>
<p class="subtitle">上传模特、上衣、裤子三张图片,AI 自动完成换装</p>
<div class="upload-grid">
<!-- 模特 -->
<div class="upload-card" id="card-model">
<input type="file" accept="image/*" id="input-model" />
<div class="upload-icon">🧍</div>
<div class="upload-label">模特图片</div>
<div class="upload-hint">节点 11 · 人物全身照</div>
<img class="preview-img" id="preview-model" alt="模特预览" />
</div>
<!-- 上衣 -->
<div class="upload-card" id="card-shirt">
<input type="file" accept="image/*" id="input-shirt" />
<div class="upload-icon">👕</div>
<div class="upload-label">上衣图片</div>
<div class="upload-hint">节点 10 · 上身服装</div>
<img class="preview-img" id="preview-shirt" alt="上衣预览" />
</div>
<!-- 裤子 -->
<div class="upload-card" id="card-pants">
<input type="file" accept="image/*" id="input-pants" />
<div class="upload-icon">👖</div>
<div class="upload-label">裤子图片</div>
<div class="upload-hint">节点 4 · 下身服装</div>
<img class="preview-img" id="preview-pants" alt="裤子预览" />
</div>
</div>
<button class="btn-run" id="btn-run" disabled>开始换装</button>
<div class="status-bar" id="status-bar"></div>
<div class="result-section" id="result-section">
<div class="result-title">换装结果</div>
<img class="result-img" id="result-img" alt="换装结果" />
<button class="btn-download" id="btn-download">下载图片</button>
</div>
<script>
const images = { model: null, shirt: null, pants: null };
function setupUpload(inputId, previewId, cardId, key) {
const input = document.getElementById(inputId);
const preview = document.getElementById(previewId);
const card = document.getElementById(cardId);
input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (ev) => {
images[key] = ev.target.result; // data:image/...;base64,...
preview.src = ev.target.result;
preview.classList.add('visible');
card.classList.add('has-image');
updateRunButton();
};
reader.readAsDataURL(file);
});
}
setupUpload('input-model', 'preview-model', 'card-model', 'model');
setupUpload('input-shirt', 'preview-shirt', 'card-shirt', 'shirt');
setupUpload('input-pants', 'preview-pants', 'card-pants', 'pants');
function updateRunButton() {
const btn = document.getElementById('btn-run');
btn.disabled = !(images.model && images.shirt && images.pants);
}
function setStatus(msg, cls = '') {
const el = document.getElementById('status-bar');
el.className = 'status-bar ' + cls;
el.innerHTML = msg;
}
function elapsed(start) {
const s = Math.round((Date.now() - start) / 1000);
return `${s}`;
}
document.getElementById('btn-run').addEventListener('click', async () => {
const btn = document.getElementById('btn-run');
const resultSection = document.getElementById('result-section');
const resultImg = document.getElementById('result-img');
btn.disabled = true;
resultSection.classList.remove('visible');
const start = Date.now();
let timer = setInterval(() => {
setStatus(`<span class="spinner"></span>正在换装中,请稍候… 已等待 ${elapsed(start)}`, 'running');
}, 1000);
setStatus('<span class="spinner"></span>正在上传图片并提交工作流…', 'running');
try {
const resp = await fetch('/try-on', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model_image: images.model,
shirt_image: images.shirt,
pants_image: images.pants,
}),
});
clearInterval(timer);
if (!resp.ok) {
const err = await resp.json().catch(() => ({ detail: resp.statusText }));
throw new Error(err.detail || '请求失败');
}
const data = await resp.json();
resultImg.src = data.result_image;
resultSection.classList.add('visible');
setStatus(`✅ 换装完成!耗时 ${elapsed(start)}`, 'success');
// 下载按钮
document.getElementById('btn-download').onclick = () => {
const a = document.createElement('a');
a.href = data.result_image;
a.download = `tryon_${Date.now()}.png`;
a.click();
};
} catch (err) {
clearInterval(timer);
setStatus(`❌ 错误:${err.message}`, 'error');
} finally {
updateRunButton();
}
});
</script>
</body>
</html>
+77
View File
@@ -0,0 +1,77 @@
@app.route('/change_cloth_base64', methods=['POST'])
def change_cloth_base64():
log_message("change_cloth_base64 called")
# 获取参数
data = request.get_json()
print(f"change_cloth_base64 input data:{data}")
if not data:
return jsonify({"ret":-1, "state":-1, 'msg': 'No JSON data provided'}), 400
human_img = data.get('human_img')
cloth_img = data.get('cloth_img')
output_format = data.get('output_format')
if not output_format:
return jsonify({"state":-1, "error": "Missing 'output_format' parameter"}), 500
if not human_img or not cloth_img:
return jsonify({"ret":-1, 'msg': 'Both human_img and cloth_img are required'}), 400
human_filename = save_base64_image(human_img, 'human')
if not human_filename:
return jsonify({"ret":-1, 'msg': 'Failed to save human image'}), 500
data['human_img'] = None
human_url = f"http://117.50.44.174:{base64_test_port}/static/imgs/{human_filename}"
# 保存服装图片
cloth_filename = save_base64_image(cloth_img, 'cloth')
if not cloth_filename:
return jsonify({"ret":-1, 'msg': 'Failed to save cloth image'}), 500
data['cloth_img'] = None
cloth_url = f"http://117.50.44.174:{base64_test_port}/static/imgs/{cloth_filename}"
data["human_url"] = human_url
data["cloth_url"] = cloth_url
kuzi_img = data.get('kuzi_img')
if kuzi_img:
kuzi_filename = save_base64_image(kuzi_img, 'kuzi')
# data['kuzi_img'] = None
kuzi_url = f"http://117.50.44.174:{base64_test_port}/static/imgs/{kuzi_filename}"
data['kuzi_url'] = kuzi_url
no2 = data.get('no2')
if not no2:
data['no2'] = False
tuodi = data.get('tuodi')
if not tuodi:
data['tuodi'] = False
if not data.get('suit'):
data['suit'] = False
if data.get('cloth_len'):
print(f'客户端传入了衣服长度: {data['cloth_len']}')
else:
print('客户端传入了衣服长度,需要ai 判断')
# 在内部调用第二个HTTP请求
try:
# 调用第二个API(可以是外部服务或自己的另一个端点)
response = requests.post(f'http://127.0.0.1:{base64_test_port}/do_change_cloth', json=data)
return Response(
response=response.content,
status=response.status_code,
headers=dict(response.headers)
)
except requests.exceptions.RequestException as e:
data['second_api_error'] = str(e)
return jsonify("error"), 500