初始化:换发型/换发色/训练发型服务

包含:
- hair_service_sd: 主服务(换发型/换发色/生发,端口8801)
- photo_service: LoRA调度+训练(端口32678)
- hair_grow_service: 调试测试页(端口8888,含4个测试页)
- 批量训练脚本(batch_train_hairstyles.py)
- 发际线mask自动识别(hairline_mask.py,4种方案)
- 手绘mask换发型(hair_swap_manual.py)
- 文档:README.md + LARGE_FILES.md + docs/

大文件(模型权重200G、训练数据123G)已排除,见 LARGE_FILES.md
OSS/COS密钥已脱敏为环境变量,原文件备份在本地
This commit is contained in:
xsl
2026-07-07 13:53:52 +08:00
commit 443cfa298f
312 changed files with 67065 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
import gradio as gr
import os,re
import numpy as np
import requests
import cv2
import base64
import json
from io import BytesIO
from PIL import Image
# api_service_url = 'http://127.0.0.1:1234'
# api_service_url = 'http://i-2.gpushare.com:53412'
api_service_url = 'http://service.aicloud.fit:7393/api/hairStyle/v1'
class ChangeHairGui():
# def __init__(self):
# a = 1
def change_hair(self, user_img, hair_img):
if user_img is None or user_img is None:
return None
#
# if user_img.shape != hair_img.shape:
# hair_img = cv2.resize(hair_img, (user_img.shape[1], user_img.shape[0]))
#
# alpha = 0.5 # 图像1的权重
# beta = 0.5 # 图像2的权重
# gamma = 0 # 亮度调整常量(通常为0)
#
# result_image = cv2.addWeighted(user_img, alpha, hair_img, beta, gamma)
# 将 Pillow 图像转换为 OpenCV 格式(BGR)
user_img = cv2.cvtColor(np.array(user_img), cv2.COLOR_RGB2BGR)
hair_img = cv2.cvtColor(np.array(hair_img), cv2.COLOR_RGB2BGR)
# 将模板图片转换为base64格式
retval, user_bytes = cv2.imencode('.jpg', user_img)
encoded_user_image = base64.b64encode(user_bytes).decode('utf-8')
retval, hair_bytes = cv2.imencode('.jpg', hair_img)
encoded_hair_image = base64.b64encode(hair_bytes).decode('utf-8')
url = api_service_url
# 请求换发型接口
payload = json.dumps({
"user_img_base64": encoded_user_image,
"hair_ref_img_base64": encoded_hair_image
})
headers = {
'Content-Type': 'application/json'
}
print('请求api_service.py发送请求!!')
response = requests.request("POST", url, headers=headers, data=payload)
print('请求api_service.py发送请求成功!!')
if response.status_code != 200:
raise RuntimeError(f"Failed to send request to API service. Status code: {response.status_code}")
ret_image_b64 = response.json().get('result')
if ret_image_b64 is None:
raise RuntimeError(f"ret image failed!")
image_array = np.frombuffer(base64.b64decode(ret_image_b64), np.uint8)
result_image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
result_image = cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB)
dst_size = max(result_image.shape[0], result_image.shape[1])
M = cv2.getRotationMatrix2D((result_image.shape[1] / 2, result_image.shape[0] / 2), 0, 1)
M[:, 2] += np.float32([dst_size / 2 - result_image.shape[1] / 2, dst_size / 2 - result_image.shape[0] / 2])
result_image = cv2.warpAffine(result_image, M, (dst_size, dst_size), borderValue=(255, 255, 255))
return result_image
def start_gui(self):
with gr.Blocks() as demo:
with gr.Row():
gr.Markdown("# 数字力场换发型效果展示")
# 换脸
with gr.Tab("换发型"):
with gr.Row():
gr.Markdown("#换发型")
with gr.Row():
with gr.Column():
user_img = gr.Image(label="请上传用户图片", type="numpy", height=384, width=384)
with gr.Column():
hair_img = gr.Image(label="请上传发型图片", type="numpy", height=384, width=384)
with gr.Column():
output_img = gr.Image(label="结果展示", type="numpy", height=384, width=384, format='png')
with gr.Column():
run_button = gr.Button(value="提交")
run_button.click(self.change_hair, inputs=[user_img, hair_img], outputs=[output_img])
demo.launch(server_name='0.0.0.0', server_port=8080)
if __name__ == '__main__':
demo = ChangeHairGui()
demo.start_gui()