Files
hair/docs/接口3-B端生发-技术实现方案.md
T
xslandClaude Opus 4.8 ce95a508c1 feat(接口3): B端生发-马克笔发际线检测+生发(替换Mock)
医生在额头用马克笔画规划发际线 → 检测该线 → 生发。检测算法源自 /home/xsl/headmark。

- hairline/marker_detect.py: 黑帽响应图(MORPH_BLACKHAT)+鬓角锚点(MediaPipe 21/251吸附)
  +skimage route_through_array 最小路径检测画线;路径平均响应阈值拒识无画线
  (headmark 调研:全局灰度阈值不可用,黑帽+Dijkstra 实测误差≤0.5px)
- hairline/mask.py: 抽出 mask_from_curve(曲线+ROI闭合),接口2/3共用
- hairline/service.py: generate_grow_b——检测→遮罩→原图重画干净线→ComfyUI生发
- app.py: /hair/grow-b 真实实现,marked+original各三选一+校验;输出
  best_hairline_image_base64(=原图)/hair_growth_image_base64/hairline_type="custom";
  无人脸或未检测到画线→1001;重活进线程池
- requirements: scikit-image==0.24.0 (⚠️锁0.24,0.25+强依赖numpy>=2会顶掉mediapipe的numpy<2)
- 文档: docs/接口3-B端生发-技术实现方案.md
- 测试: test_marker.py(检测/拒识/辅助) + test_api grow-b(mock ComfyUI),42全绿

实测(5090): grow-b ~6.4s,生发图把额头发际线补到医生画线、清除划线、人物保持。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 00:08:05 +08:00

94 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 接口 3:B 端生发 — 技术实现方案(马克笔发际线检测 + 生发)
> 在 **高性能 workerGPU 机)** 实现,与接口 1/2 同机。对外经网关代理。
> B 端:医生在患者额头**用马克笔画出规划的发际线**,拍照上传。系统**检测这条手绘线**,
> 据此生成生发图。检测算法移植自 `/home/xsl/headmark` 的调研结论(黑帽 + Dijkstra)。
---
## 0. 契约(对齐 `接口文档.md` 接口3,不变)
`POST /api/v1/hair/grow-b`
| 输入 | 说明 |
|------|------|
| `marked_image_*` | 已划线(医生标注发际线)的图,三选一,必填 |
| `original_image_*` | 原始用户照片,三选一,必填 |
| 输出 data | 决策 |
|-----------|------|
| `best_hairline_image_url` | **= 原图 original** 原样返回(worker 返回 `best_hairline_image_base64` |
| `hair_growth_image_url` | **生发后图片**ComfyUIworker 返回 `hair_growth_image_base64` |
| `hairline_type` | 固定 **`"custom"`**(手绘定制) |
> 落盘改 URL 由网关做(架构同接口1/2)。
---
## 1. 马克笔发际线检测(核心,源自 headmark 调研)
headmark `docs/detection_research.md` 结论:全局灰度阈值不可用(笔迹平均灰度反而高于阈值、
与皮肤阴影分布重叠);推荐 **黑帽响应图 + 端点锚定 Dijkstra 最小路径**,实测误差 ≤0.5px(GT锚点)。
本项目用 **MediaPipe 锚点**(非 GT)实测平均 3.2px、中位 0px —— 对生成遮罩足够(线会膨胀成带)。
```
detect_marker_hairline(marked_bgr, landmarks, parse_map):
[1] ROI = forehead_upper_region(landmarks) ∩ head_silhouette(parse_map) # 复用接口2 mask.py
[2] 黑帽响应 bh = MORPH_BLACKHAT(gray, ksize=max(15,int(w*0.025)|1))ROI 外置 0
[3] 锚点 = MediaPipe 21(左鬓角)/251(右鬓角),各自小窗口(≈w*3%)内吸附到 bh 最大处
[4] 代价 cost = bh.max()-bh+1ROI 外设 1e6
path = skimage.graph.route_through_array(cost, 左锚, 右锚, fully_connected, geometric)
[5] 拒识:path 平均 bh 响应 < 阈值(可调) → None(上层返回 1001 "未检测到发际线划线"
return path # (N,2) row,col
```
- 依赖:**`scikit-image==0.24.0`**。⚠️ 0.25+ 强依赖 numpy≥2,会顶掉 mediapipe 的 numpy<2 →
mediapipe/SegFormer 全崩。**必须锁 0.24.x**。
- 复用接口2`forehead_upper_region` / `head_silhouette``hairline/mask.py`)、SegFormer / MediaPipe 单例。
## 2. 遮罩 + 原图重画干净线
- **遮罩**path → 画成 curve_mask → 复用接口2 `_above_curve_region` + `head` + `_clean_mask`
得到"发际线以上闭合区域"。
- **ComfyUI 输入图**:用 **原图 original**(按需缩放到 marked 尺寸对齐坐标),**重画一条干净黑线**
(检测 path 膨胀成线宽),避免医生手绘的毛刺/杂线干扰生成。
- 合成 RGBARGB=重画线的原图,alpha=255−mask(透明=重绘区)。复用 `compose_comfy_rgba`
## 3. 生发(复用接口2 ComfyUI 客户端)
`hairline/comfyui.run(rgba_png)` → 跑 `add_hair.json`(Flux-2)→ 生发图 PNG。同步。
## 4. worker handler`/api/v1/hair/grow-b`
```
1. marked + original 各三选一取图(复用 resolve_image_bytes+ 校验(大小/解码/分辨率)
2. 在 marked 上:landmarks(MediaPipe)+parse(SegFormer) → detect_marker_hairline
- 无人脸 → 1001;未检测到画线 → 1001 "未检测到发际线划线"
3. 遮罩 + 原图重画线 → RGBA → comfyui.run → 生发图
4. return ok({ best_hairline_image_base64: 原图, hair_growth_image_base64: 生发图,
hairline_type: "custom" })
异常 → 1007;重活 run_in_threadpool。
```
## 5. 开发步骤
| 阶段 | 内容 | 验证 |
|------|------|------|
| **M1 检测** | `hairline/marker_detect.py`(黑帽+锚点+Dijkstra+拒识) | headmark test_image:检测线贴合真值;无线图被拒识 |
| **M2 遮罩+重画** | path→遮罩(复用) + 原图重画干净线 + RGBA 合成 | 目视:干净线在原图、遮罩贴合 |
| **M3 接 app** | grow-b 真实实现 + 输出字段 + 1001 | curlbest=原图/grown 合法PNG/type=custom;无线→1001 |
| **M4 测试** | 检测/mask 单测 + mock-ComfyUI 集成 + 真机冒烟 | pytest 绿;真机出生发图 |
## 6. 风险
1. **锚点偏差/路径端点偏移**MediaPipe 21/251 吸附后仍可能在鬓角端有偏移(实测 max~42px,少数点)。
膨胀成带 + 遮罩闭合可吸收;必要时改进吸附窗口或端点截断。
2. **没画线/画线极浅**:靠拒识阈值(路径平均黑帽响应)兜底,阈值需在更多真实图上标定。
3. **marked 与 original 尺寸/对齐不一致**:按 marked 坐标系处理,original 缩放对齐;若两图非同源(不同姿态)会错位——约定二者为"同一张照片的划线版/原始版"。
4. **抬头纹/眉毛/发丝干扰**:黑帽 + ROI + Dijkstra 平滑已大幅抑制(调研验证抬头纹零干扰),极端情况可在代价图抑制头发区域。
---
> **文档版本**: v1.0 **创建日期**: 2026-06-15 检测来源: headmark(黑帽+Dijkstra)|
> 生发: 复用接口2 ComfyUI(add_hair.json) 运行位置: worker(GPU) + 本机 ComfyUI(8182)