接口11:发际线生发(接口9遮罩 + change_hair换发型/区域生发 + 按遮罩羽化贴回)+ 分步可视化测试页

- 端点 POST /api/v1/hairline/grow(app.py,纯新增,不影响接口1-5)
- 编排模块 face_analysis/hairline_grow.py:复用接口9 遮罩 → HTTP 调 change_hair(8801) → 按遮罩贴回
- 双生成后端 gen_backend:swaphair(换发型LoRA) / hairgrow(区域生发inpaint)
- swap_mode:ext_mask(接口9遮罩作换发型遮罩) / as_is;融合 feather/alpha_gradient/seamless
- 参数全在测试页可调;可视化按算法文档4步:最终遮罩→生成全帧→严格贴回→接缝融合
- 附启停脚本 scripts/restart_if11_backends.sh、算法文档、测试图

注:change_hair 侧 swapHair 的 ext_mask/denoising_strength 改造在 change_hair 仓库,向后兼容。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
xsl
2026-07-09 22:06:17 +08:00
co-authored by Claude Opus 4.8
parent 3eb60bddc5
commit 0ddfa83743
6 changed files with 883 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env bash
# 接口11(发际线生发)所需的 change_hair 后端服务启停脚本。
#
# webui(57860) + photo_service(32678) + hair_service_sd(8801)
# —— 不需要 ComfyUI。
#
# hair_service_sd 会被【强制重启】以加载接口11 的 ext_mask 改动;
# webui / photo_service 仅在未运行时才拉起(已在跑则不动,避免 webui 重新加载模型)。
#
# 用法:
# bash scripts/restart_if11_backends.sh # 确保三个服务就绪(强制重启 8801)
# bash scripts/restart_if11_backends.sh status # 只看状态
set -u
PROJ="/home/xsl/change_hair/project"
LOGDIR="$PROJ/logs"
PIDD="$LOGDIR/pids"
PY_SD="/home/xsl/miniconda3/envs/sdwebui/bin/python"
PY_PHOTO="/home/xsl/miniconda3/envs/py310/bin/python"
PY_HAIR="/home/xsl/miniconda3/envs/my_hair/bin/python"
mkdir -p "$LOGDIR" "$PIDD"
export CRYPTOGRAPHY_OPENSSL_NO_LEGACY=1
export CUDA_VISIBLE_DEVICES=0
export APP_WORKER_ID=1
port_up() { ss -ltn 2>/dev/null | grep -q ":$1 "; }
wait_port() { # $1=port $2=label $3=timeout_s
local p="$1" label="$2" to="${3:-90}" i=0
echo -n " 等待 $label (端口 $p) "
while [ "$i" -lt "$to" ]; do
if port_up "$p"; then echo " ✓ 就绪"; return 0; fi
echo -n "."; sleep 2; i=$((i+2))
done
echo " ✗ 超时(看日志:$LOGDIR/*.log"; return 1
}
status() {
echo "=== 服务状态 $(date '+%H:%M:%S') ==="
for pair in "webui:57860" "photo_service:32678" "hair_service_sd:8801"; do
local name="${pair%%:*}" port="${pair##*:}"
if port_up "$port"; then echo "$name (端口 $port 就绪)"; else echo "$name (端口 $port 未监听)"; fi
done
echo " worker 8187: $(curl -s -m3 http://127.0.0.1:8187/health 2>/dev/null || echo '未响应')"
}
if [ "${1:-}" = "status" ]; then status; exit 0; fi
# 1) webui —— 仅未运行时启动(模型加载慢,别乱重启)
if pgrep -f "webui.py.*57860" >/dev/null; then
echo "webui 已在运行,跳过"
else
echo "启动 webui (57860)..."
( cd "$PROJ/onediff/stable-diffusion-webui" && \
HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 \
nohup "$PY_SD" webui.py --api --listen --xformers --port 57860 > "$LOGDIR/webui.log" 2>&1 & \
echo $! > "$PIDD/webui.pid" )
echo " webui PID=$(cat "$PIDD/webui.pid" 2>/dev/null)"
fi
# 2) photo_service —— 仅未运行时启动
if pgrep -f "lora_train_service_1.py" >/dev/null; then
echo "photo_service 已在运行,跳过"
else
echo "启动 photo_service (32678)..."
( cd "$PROJ/photo_service" && \
nohup "$PY_PHOTO" -u lora_train_service_1.py > "$LOGDIR/photo_service.log" 2>&1 & \
echo $! > "$PIDD/photo_service.pid" )
echo " photo_service PID=$(cat "$PIDD/photo_service.pid" 2>/dev/null)"
fi
# 3) hair_service_sd —— 强制重启(加载接口11 的 ext_mask 改动)
echo "强制重启 hair_service_sd (8801)..."
pkill -f "run_copy_cost_colorb64" 2>/dev/null
sleep 2
( cd "$PROJ/hair_service_sd" && \
nohup "$PY_HAIR" run_copy_cost_colorb64.py > "$LOGDIR/hair_service_if11.log" 2>&1 & \
echo $! > "$PIDD/hair_service.pid" )
echo " hair_service_sd PID=$(cat "$PIDD/hair_service.pid" 2>/dev/null)"
echo "--- 等待就绪 ---"
wait_port 57860 webui 180
wait_port 32678 photo_service 60
wait_port 8801 hair_service_sd 120
echo
status
echo
echo "提示:hair_service_sd 端口就绪后,模型可能还需几秒;日志出现 '[HAIR_INIT] All init done' 即完全可用:"
echo " tail -f $LOGDIR/hair_service_if11.log"