save code
@@ -0,0 +1,108 @@
|
||||
# change_app.py API 文档
|
||||
|
||||
## 1. GET /
|
||||
|
||||
返回主页静态文件 `index.html`。
|
||||
|
||||
---
|
||||
|
||||
## 2. POST /change_cloth
|
||||
|
||||
**推荐入口**,带 Redis 队列排队,避免并发冲突。
|
||||
|
||||
### 请求参数(JSON)
|
||||
|
||||
| 参数 | 类型 | 必选 | 说明 |
|
||||
|------|------|------|------|
|
||||
| human_url | string | 是 | 人物图片 URL |
|
||||
| cloth_url | string | 是 | 服装图片 URL |
|
||||
| output_format | string | 是 | 输出格式,包含 `"base64"` 则返回 base64,否则返回 URL |
|
||||
| no2 | bool | 否 | true=跳过第一步(脱衣/生成写真),直接换衣;默认 false |
|
||||
| tuodi | bool | 否 | true=使用拖地款工作流;默认 false |
|
||||
| kuzi | bool | 否 | 是否同时换裤子(配合 kuzi_url);默认 false |
|
||||
| cloth_len | string | 否 | 手动指定服装长度(如"膝盖""拖地"等),不传则 AI 自动判断 |
|
||||
| suit | bool | 否 | true=使用 Gemini 模型换装(套装模式);默认 false |
|
||||
|
||||
### 响应
|
||||
|
||||
```json
|
||||
// 成功(URL 模式)
|
||||
{
|
||||
"ret": 0,
|
||||
"state": 0,
|
||||
"msg": "success",
|
||||
"url": "https://xiangsilian.oss-cn-beijing.aliyuncs.com/xxx.jpg",
|
||||
"second_url": "换衣前中间步骤图 URL",
|
||||
"first_url": "第一步(脱衣/写真)图 URL",
|
||||
"is_girl": true
|
||||
}
|
||||
|
||||
// 成功(base64 模式)
|
||||
{
|
||||
"ret": 0,
|
||||
"state": 0,
|
||||
"msg": "success",
|
||||
"data": "data:image/jpeg;base64,...",
|
||||
"second_step_data": "...",
|
||||
"first_step_data": "...",
|
||||
"is_girl": true
|
||||
}
|
||||
|
||||
// 失败
|
||||
{ "ret": -1, "state": -1, "msg": "错误信息" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. POST /change_cloth_base64
|
||||
|
||||
与 `/change_cloth` 功能相同,图片以 Base64 格式传入。
|
||||
|
||||
### 请求参数(JSON)
|
||||
|
||||
| 参数 | 类型 | 必选 | 说明 |
|
||||
|------|------|------|------|
|
||||
| human_img | string | 是 | 人物图片 base64(带 data: 头,如 `data:image/jpeg;base64,...`) |
|
||||
| cloth_img | string | 是 | 服装图片 base64 |
|
||||
| output_format | string | 是 | 同上 |
|
||||
| kuzi_img | string | 否 | 裤子图片 base64 |
|
||||
| no2 | bool | 否 | 同上 |
|
||||
| tuodi | bool | 否 | 同上 |
|
||||
| suit | bool | 否 | 同上 |
|
||||
| cloth_len | string | 否 | 同上 |
|
||||
|
||||
### 响应
|
||||
|
||||
同 `/change_cloth`。
|
||||
|
||||
---
|
||||
|
||||
## 4. POST /do_change_cloth
|
||||
|
||||
**直接调用**,无队列保护,通常由内部或 `/change_cloth_base64` 调用,不建议外部直接使用。
|
||||
|
||||
### 请求参数(JSON)
|
||||
|
||||
| 参数 | 类型 | 必选 | 说明 |
|
||||
|------|------|------|------|
|
||||
| human_url | string | 是 | 人物图片 URL |
|
||||
| cloth_url | string | 是 | 服装图片 URL |
|
||||
| output_format | string | 是 | 同上 |
|
||||
| no2 | bool | 否 | 同上 |
|
||||
| tuodi | bool | 否 | 同上 |
|
||||
| kuzi_url | string | 否 | 裤子图片 URL |
|
||||
| cloth_len | string | 否 | 同上 |
|
||||
| suit | bool | 否 | 同上 |
|
||||
|
||||
### 响应
|
||||
|
||||
同 `/change_cloth`。
|
||||
|
||||
---
|
||||
|
||||
## 备注
|
||||
|
||||
- `cloth_len` 可选值:`胸、腰、跨、大腿、膝盖、小腿、脚踝、拖地、裤子、难以辨认`
|
||||
- 若服装图片中检测到人物,`cloth_len` 自动设为 `拖地`
|
||||
- 结果图片同时上传至阿里云 OSS(bucket: `xiangsilian`,region: 北京)
|
||||
- 服务默认监听端口由 `config.py` 中的 `base64_test_port` 决定
|
||||
@@ -66,7 +66,9 @@ def get_redis_conn():
|
||||
"""获取 Redis 连接"""
|
||||
return redis.Redis(connection_pool=redis_pool)
|
||||
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__, static_folder='static', static_url_path='/static')
|
||||
from flask_cors import CORS
|
||||
CORS(app)
|
||||
|
||||
client = Ark(
|
||||
# 此为默认路径,您可根据业务所在地域进行配置
|
||||
@@ -225,10 +227,16 @@ def generate_from_face(human_name, sex_girl):
|
||||
|
||||
prompt["93"]["inputs"]["image"] = human_name
|
||||
|
||||
# if sex_girl:
|
||||
# prompt["6"]["inputs"]["text"] = "asian girl,knee-length shot ,model pose,hands are at the sides of the body,smile,wearing black tube top and micro skirt,simple ,white background,32k,high detail,and face is illuminated by soft side light and natural light. The sunlight spills over the white wall behind."
|
||||
# else:
|
||||
# prompt["6"]["inputs"]["text"] = "asian man,tall,knee-length shot,model pose,hands are at the sides of the body,smile,wearing black short pants,simple white background,The sunlight spills over the white wall behind,32k."
|
||||
|
||||
if sex_girl:
|
||||
prompt["6"]["inputs"]["text"] = "asian girl,knee-length shot ,model pose,hands are at the sides of the body,smile,wearing black tube top and micro skirt,simple ,white background,32k,high detail,and face is illuminated by soft side light and natural light. The sunlight spills over the white wall behind."
|
||||
prompt["6"]["inputs"]["text"] = "asian girl,knee-length shot,model pose,hands are at the sides of the body,smile,wearing black tube top and micro skirt,solid very dark gray background,32k,high detail,and face is illuminated by soft side light and natural light."
|
||||
else:
|
||||
prompt["6"]["inputs"]["text"] = "asian man,tall,knee-length shot,model pose,hands are at the sides of the body,smile,wearing black short pants,simple white background,The sunlight spills over the white wall behind,32k."
|
||||
prompt["6"]["inputs"]["text"] = "asian man,tall,knee-length shot,model pose,hands are at the sides of the body,smile,shirtless,wearing black short pants,very dark gray background,solid dark gray background,32k,"
|
||||
|
||||
|
||||
#out put name
|
||||
out_img_name = str(uuid.uuid4())[:8]
|
||||
@@ -639,9 +647,61 @@ def process_change_banana(human_filename, cloth_filename, output_format):
|
||||
"msg":"failure"
|
||||
})
|
||||
|
||||
def process_change_cloth(human_filename, cloth_filename, output_format, img_url, human_url, no2, tuodi, kuzi_url, cloth_len):
|
||||
def process_change_cloth(human_filename, cloth_filename, output_format, img_url, human_url, no2, tuodi, kuzi_url, cloth_len, kuzi_img=None):
|
||||
w,h = get_image_dimensions(f'{APP_ROOT}/../input/{human_filename}')
|
||||
|
||||
if kuzi_url:
|
||||
print(f'换裤子 kuzi_url:{kuzi_url}')
|
||||
|
||||
human_json_str = GetHumanDesDesc(human_url)
|
||||
print(f"human_json_str {human_json_str}")
|
||||
human_json_data = json.loads(human_json_str)
|
||||
sex_type = human_json_data['性别']
|
||||
sex_girl = True
|
||||
if '男' in sex_type:
|
||||
sex_girl = False
|
||||
|
||||
new_data = {
|
||||
"model_image": image_to_base64(f'{APP_ROOT}/../input/{human_filename}', False),
|
||||
"shirt_image": image_to_base64(f'{APP_ROOT}/../input/{cloth_filename}', False),
|
||||
"pants_image": kuzi_img if kuzi_img else image_to_base64(save_image_from_url(kuzi_url), False),
|
||||
}
|
||||
response = requests.post(f'http://117.50.44.174:47698/try-on', json=new_data)
|
||||
result_image_data = response.json().get("result_image", "")
|
||||
if result_image_data:
|
||||
print(f"result_image_data: {result_image_data[:30]}...") # 打印前30个字符以验证数据格式
|
||||
if 'base64' in output_format:
|
||||
return jsonify({
|
||||
"ret":0,
|
||||
"state": 0,
|
||||
"msg":"success",
|
||||
"is_girl":sex_girl,
|
||||
"second_step_data":new_data["model_image"],
|
||||
"first_step_data":new_data["model_image"],
|
||||
"data":result_image_data
|
||||
})
|
||||
else:
|
||||
result_image_bytes = base64.b64decode(result_image_data.split(',')[1])
|
||||
result_image_stream = BytesIO(result_image_bytes)
|
||||
image = Image.open(result_image_stream)
|
||||
jpg_name = f"{uuid.uuid4()}.png"
|
||||
jpg_path_name = f'{APP_ROOT}/static/imgs/{jpg_name}'
|
||||
image.save(jpg_path_name, quality=90)
|
||||
upload_to_oss(jpg_path_name, jpg_name)
|
||||
https_url = f'https://xiangsilian.oss-cn-beijing.aliyuncs.com/{jpg_name}'
|
||||
|
||||
return jsonify({
|
||||
"ret":0,
|
||||
"state": 0,
|
||||
"msg":"success",
|
||||
"second_url":https_url,
|
||||
"first_url":https_url,
|
||||
"is_girl":sex_girl,
|
||||
"url":https_url
|
||||
})
|
||||
|
||||
|
||||
|
||||
out_put_name, out_human_name, is_girl, msg = change(human_filename, cloth_filename, w, h, img_url, human_url, no2, tuodi, cloth_len)
|
||||
if out_put_name == None:
|
||||
print(f'Failed to change cloth {msg}')
|
||||
@@ -748,7 +808,13 @@ def save_image_from_url(image_url):
|
||||
except Exception as e:
|
||||
print(f"Error saving image from URL: {e}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""返回主页"""
|
||||
from flask import send_from_directory
|
||||
return send_from_directory('static', 'index.html')
|
||||
|
||||
@app.route('/do_change_cloth', methods=['POST'])
|
||||
def do_change_cloth():
|
||||
@@ -801,7 +867,7 @@ def do_change_cloth():
|
||||
if 'cloth_len' in data:
|
||||
cloth_len = data['cloth_len']
|
||||
try:
|
||||
return process_change_cloth(human_filename, cloth_filename, output_format, cloth_url, human_url, no2, tuodi, kuzi_url, cloth_len)
|
||||
return process_change_cloth(human_filename, cloth_filename, output_format, cloth_url, human_url, no2, tuodi, kuzi_url, cloth_len, data.get("kuzi_img") )
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
print(f"错误详情:{e}")
|
||||
@@ -912,7 +978,7 @@ def change_cloth_base64():
|
||||
return jsonify({"ret":-1, 'msg': 'Failed to save human image'}), 500
|
||||
data['human_img'] = None
|
||||
|
||||
human_url = f"http://112.126.94.241:{base64_test_port}/static/imgs/{human_filename}"
|
||||
human_url = f"http://117.50.44.174:{base64_test_port}/static/imgs/{human_filename}"
|
||||
|
||||
# 保存服装图片
|
||||
cloth_filename = save_base64_image(cloth_img, 'cloth')
|
||||
@@ -920,7 +986,7 @@ def change_cloth_base64():
|
||||
return jsonify({"ret":-1, 'msg': 'Failed to save cloth image'}), 500
|
||||
data['cloth_img'] = None
|
||||
|
||||
cloth_url = f"http://112.126.94.241:{base64_test_port}/static/imgs/{cloth_filename}"
|
||||
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
|
||||
@@ -928,8 +994,8 @@ def change_cloth_base64():
|
||||
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://112.126.94.241:{base64_test_port}/static/imgs/{kuzi_filename}"
|
||||
# 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')
|
||||
@@ -953,7 +1019,7 @@ def change_cloth_base64():
|
||||
# 在内部调用第二个HTTP请求
|
||||
try:
|
||||
# 调用第二个API(可以是外部服务或自己的另一个端点)
|
||||
response = requests.post(f'http://112.126.94.241:{base64_test_port}/do_change_cloth', json=data)
|
||||
response = requests.post(f'http://117.50.44.174:{base64_test_port}/do_change_cloth', json=data)
|
||||
|
||||
return Response(
|
||||
response=response.content,
|
||||
@@ -967,4 +1033,4 @@ def change_cloth_base64():
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host="0.0.0.0", port=8888, debug=True)
|
||||
app.run(host="0.0.0.0", port=base64_test_port, debug=True)
|
||||
@@ -347,7 +347,7 @@ def change_cloth_base64():
|
||||
if not cloth_filename:
|
||||
return jsonify({"ret":-1, 'msg': 'Failed to save cloth image'}), 500
|
||||
|
||||
img_url = f"http://112.126.94.241:18888/static/imgs/{cloth_filename}"
|
||||
img_url = f"http://117.50.44.174:18888/static/imgs/{cloth_filename}"
|
||||
|
||||
return process_change_cloth(human_filename, cloth_filename, output_format, img_url, is_generated)
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 277 KiB |
@@ -92,12 +92,12 @@
|
||||
<div class="form-group">
|
||||
<label for="clothImgUrl">服装图片URL:</label>
|
||||
<input type="text" id="clothImgUrl" name="cloth_img"
|
||||
value="http://112.126.94.241:18888/static/imgs/cloth_20250713_092536_279335d2.jpg"
|
||||
value="http://117.50.44.174:18888/static/imgs/cloth_20250713_092536_279335d2.jpg"
|
||||
placeholder="输入服装图片的URL" required>
|
||||
<div class="preview">
|
||||
<div class="preview-item">
|
||||
<span>URL图片预览:</span>
|
||||
<img id="urlPreview" src="http://112.126.94.241:18888/static/imgs/cloth_20250713_092536_279335d2.jpg"
|
||||
<img id="urlPreview" src="http://117.50.44.174:18888/static/imgs/cloth_20250713_092536_279335d2.jpg"
|
||||
alt="URL图片预览" onerror="this.style.display='none'">
|
||||
</div>
|
||||
</div>
|
||||
@@ -149,7 +149,7 @@
|
||||
statusDiv.className = "";
|
||||
|
||||
const formData = new FormData(this);
|
||||
const apiUrl = "http://47.94.244.112:19001/process-image";
|
||||
const apiUrl = "http://117.50.44.174:19001/process-image";
|
||||
|
||||
fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
|
||||
|
After Width: | Height: | Size: 709 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 709 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -257,7 +257,7 @@
|
||||
console.log("准备发送的数据:", data); // 调试用
|
||||
|
||||
// 调用API
|
||||
fetch('http://112.126.94.241:28888/change_cloth_base64', {
|
||||
fetch('http://117.50.44.174:28888/change_cloth_base64', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -94,12 +94,12 @@
|
||||
|
||||
<div class="input-group">
|
||||
<label for="human-url">人体图片URL:</label>
|
||||
<input type="text" id="human-url" value="http://112.126.94.241:18888/static/imgs/human_20250713_091718_fb1f8a05.jpg">
|
||||
<input type="text" id="human-url" value="http://117.50.44.174:18888/static/imgs/human_20250713_091718_fb1f8a05.jpg">
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="cloth-url">衣服图片URL:</label>
|
||||
<input type="text" id="cloth-url" value="http://112.126.94.241:18888/static/imgs/cloth_20250713_092536_279335d2.jpg">
|
||||
<input type="text" id="cloth-url" value="http://117.50.44.174:18888/static/imgs/cloth_20250713_092536_279335d2.jpg">
|
||||
</div>
|
||||
|
||||
<button id="submit-btn">提交处理</button>
|
||||
@@ -136,7 +136,7 @@
|
||||
|
||||
try {
|
||||
// 发送POST请求
|
||||
const response = await fetch('http://112.126.94.241:18888/change_cloth', {
|
||||
const response = await fetch('http://117.50.44.174:18888/change_cloth', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -246,7 +246,7 @@
|
||||
// 获取服务器状态数据
|
||||
async function fetchServerStatus() {
|
||||
try {
|
||||
const response = await fetch('http://112.126.94.241:18018/get_server_state');
|
||||
const response = await fetch('http://117.50.44.174:18018/get_server_state');
|
||||
if (!response.ok) throw new Error('网络响应不正常');
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
|
||||
@@ -27,7 +27,7 @@ def image_to_base64(file_path, mime_type=None):
|
||||
return f"data:{mime_type};base64,{encoded_string}"
|
||||
|
||||
if __name__ == '__main__':
|
||||
url = 'http://112.126.94.241:18018/change_cloth_base64'
|
||||
url = 'http://117.50.44.174:18018/change_cloth_base64'
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
img64_human_str = image_to_base64("/home/xsl/code/ComfyUI/input/girl_full.jpg")
|
||||
data = {}
|
||||
|
||||