# 接口3 B端生发 — 实现文档 > 文档日期:2026-07-18 --- ## 一、接口概述 **接口3** 是 B端(医生/操作端)生发接口。医生在用户照片上手动用马克笔画出发际线后,只需上传这一张划线图,系统自动检测划线 → 生成遮罩 → 送 ComfyUI 生发,返回「植发3个月」效果图。 **与接口2 的核心区别**: | 特性 | 接口2(C端生发) | 接口3(B端生发) | |------|----------------|----------------| | 输入 | 原始照片 | 划线图(含手绘线) | | 发际线来源 | 系统按发型模板自动生成 | 医生手绘标注 | | 发型类型 | ellipse/flower/heart/straight/wave | custom(自定义) | | 中间步骤 | extract_context + swapHair + ComfyUI重绘 | 划线检测 + 遮罩 + ComfyUI生发 | | 是否调 change_hair | 是(女性流程) | 否 | | ComfyUI 工作流 | 0716add-hair-api.json(重绘) | add_hair.json(生发) | | 典型耗时 | ~11s | ~6-8s | --- ## 二、接口定义 ### 路由 ``` POST /api/v1/hair/grow-b ``` ### 入参 | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `marked_image_file` | UploadFile | 三选一 | 划线图片文件(JPG/PNG) | | `marked_image_url` | str | 三选一 | 划线图片 URL | | `marked_image_base64` | str | 三选一 | 划线图片 base64 | | `use_mask` | bool | 否(默认True) | 是否自动检测划线并建遮罩。False时跳过检测,直接送划线图 | | `prompt` | str | 否 | ComfyUI 提示词,默认"补充遮罩区域的头发,加一点美颜" | ### 返回 ```json { "code": 0, "message": "success", "data": { "hair_growth_image_base64": "iVBORw0KGgo...(生发图 JPG base64)", "hairline_type": "custom" } } ``` 错误码: - `1001`: 无法识别人像 / 未检测到发际线划线 - `1007`: 处理失败 - `1008`: 图片格式不支持 --- ## 三、完整调用链 ``` POST /api/v1/hair/grow-b │ ├─ app.py hair_grow_b() [app.py:929] │ ├─ resolve_image_bytes() → marked_raw 解析图片(file/url/base64三选一) │ ├─ cv2.imdecode → marked_bgr 解码为 BGR │ └─ run_in_threadpool(generate_grow_b, ...) │ ├─ service.py generate_grow_b(marked_bgr, use_mask, prompt) [service.py:381] │ │ │ ├─ 步骤1:人脸检测 + 头发分割(仅 use_mask=True 时) │ │ ├─ get_landmarker().detect(rgb) MediaPipe 478点人脸检测 │ │ │ → landmarks(无人脸返回 no_face) │ │ ├─ get_parser().parse(rgb) SegFormer 面部分割(CPU ~0.9s) │ │ │ → parse_map(int label map) │ │ │ │ ├─ 步骤2:手绘发际线检测(仅 use_mask=True 时) │ │ ├─ detect_marker_hairline(marked_bgr, landmarks, parse_map) │ │ │ │ [marker_detect.py:41] │ │ │ ├─ forehead_upper_region(landmarks) 额头上部 ROI │ │ │ ├─ head_silhouette(parse_map) 头部轮廓 ROI │ │ │ ├─ _blackhat(gray) 黑帽变换(响应比邻域暗的细结构) │ │ │ ├─ _snap_anchor(bh, 左鬓角21) 左锚点吸附 │ │ │ ├─ _snap_anchor(bh, 右鬓角251) 右锚点吸附 │ │ │ ├─ route_through_array(cost, 左, 右) Dijkstra最小代价路径 │ │ │ └→ path (N,2) row,col(拒识返回 None → no_line) │ │ │ │ │ ├─ path_to_curve_mask(path) 路径→曲线mask(uint8 0/255) │ │ └─ mask_from_curve(curve_mask, landmarks, parse_map) │ │ │ [mask.py] │ │ ├─ _above_curve_region(curve_mask) 曲线以上区域 │ │ ├─ cv2.morphologyEx(闭运算) 填洞 │ │ ├─ 最大连通域 │ │ └─ 高斯羽化 → mask (uint8 0-255) │ │ │ ├─ 步骤3:合成 RGBA PNG │ │ ├─ compose_comfy_rgba(marked_bgr, mask) RGB=原图,alpha=255×(1-mask) │ │ └─ PNG 编码 → rgba_png_bytes │ │ │ └─ 步骤4:ComfyUI 生发 │ └─ comfyui.run(rgba_png_bytes, prompt) [comfyui.py:87] │ ├─ 上传图片到 ComfyUI /upload/image │ ├─ 加载工作流 add_hair.json │ ├─ 替换节点26输入图 + 节点6随机seed + 节点60提示词 │ ├─ POST /prompt 提交工作流 │ ├─ 轮询 /history/{prompt_id}(间隔0.2s) │ └─ GET /view 取回输出 PNG → grown_png │ └─ 返回 {"grown_png": bytes, "status": "ok"} ``` --- ## 四、用到的模型和外部服务 | 模型/服务 | 用途 | 位置 | 设备 | |----------|------|------|------| | **FaceLandmarker** (MediaPipe) | 478点人脸检测 | hairline/face_landmarks.py | CPU | | **FaceParser** (SegFormer) | 面部分割(hair/skin/...) | hairline/face_parsing.py | CPU (5090不兼容cu121) | | **ComfyUI** (Flux-2) | 生发图生成 | hairline/comfyui.py → :8188 | GPU | **注意**:接口3 **不调用** change_hair 服务(:8801),不需要 swapHair。这是它与接口2女性流程的关键区别。 --- ## 五、核心算法:手绘发际线检测 ### 5.1 为什么不用简单阈值? 手绘马克笔线条的灰度值与皮肤阴影、抬头纹等重叠,全局阈值无法区分。采用**黑帽变换 + Dijkstra最小路径**方案。 ### 5.2 黑帽变换(Black Hat) ```python kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (k, k)) bh = cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, kernel) ``` 黑帽 = 闭运算 − 原图,响应"比局部邻域暗的细结构"(即马克笔线条),对抬头纹/眉毛/发丝鲁棒。 ### 5.3 Dijkstra 最小代价路径 1. **ROI 限定**:额头上部 ∩ 头部轮廓(排除背景) 2. **锚点**:左鬓角(21) / 右鬓角(251) MediaPipe 关键点 3. **代价图**:`cost = (bh.max() - bh) + 1.0`,ROI外设 1e6 4. **路径**:`route_through_array(cost, 左锚, 右锚)` — skimage 的 Dijkstra 实现 ### 5.4 拒识机制 路径平均黑帽响应 < 8.0 → 判定"未画线",返回 `no_line`。 --- ## 六、与接口1、接口2 的对比 | 维度 | 接口1 | 接口2 | 接口3 | |------|-------|-------|-------| | 功能 | 四庭七眼测量 | C端生发(5种发际线) | B端生发(手绘线) | | 路由 | /api/v1/face/measure | /api/v1/hair/grow | /api/v1/hair/grow-b | | 输入 | 正面照 | 正面照 | 划线图 | | MediaPipe | ✅ | ✅ | ✅ | | SegFormer | ✅ | ✅ | ✅ | | change_hair | ❌ | ✅(女性) | ❌ | | ComfyUI | ❌ | ✅(Flux-2重绘) | ✅(Flux-2生发) | | 典型耗时 | ~2s | ~11s | ~6-8s | | ComfyUI工作流 | — | 0716add-hair-api.json | add_hair.json | --- ## 七、测试 - **测试页面**:[static/test_interface3.html](file:///home/ubuntu/hair/static/test_interface3.html) - **测试图片**:[image/girl_img/girl13.jpg](file:///home/ubuntu/hair/image/girl_img/girl13.jpg)(需手动在图上画发际线后作为划线图上传)