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>
This commit is contained in:
+15
-13
@@ -83,27 +83,29 @@ def _clean_mask(mask: np.ndarray, w: int) -> np.ndarray:
|
||||
return out
|
||||
|
||||
|
||||
def mask_from_curve(curve_mask: np.ndarray, landmarks_mp: np.ndarray,
|
||||
parse_map: np.ndarray) -> np.ndarray:
|
||||
"""由发际线曲线 + ROI(额头上部 ∩ 头部) 围成"曲线以上"闭合遮罩(uint8 0..255)。
|
||||
|
||||
接口2(模板渲染曲线) 与 接口3(检测路径曲线) 共用。
|
||||
"""
|
||||
h, w = curve_mask.shape[:2]
|
||||
roi = cv2.bitwise_and(forehead_upper_region(landmarks_mp, w, h),
|
||||
head_silhouette(parse_map))
|
||||
above = _above_curve_region(curve_mask, h, w)
|
||||
return _clean_mask(cv2.bitwise_and(roi, above), w)
|
||||
|
||||
|
||||
def build_inpaint_mask(photo_bgr: np.ndarray, landmarks_mp: np.ndarray,
|
||||
parse_map: np.ndarray, points502: np.ndarray,
|
||||
black_texture_rgba: np.ndarray):
|
||||
"""返回 (marked_bgr 划线图, mask uint8 0..255 重绘区)。"""
|
||||
"""接口2:返回 (marked_bgr 划线图, mask uint8 0..255 重绘区)。"""
|
||||
h, w = photo_bgr.shape[:2]
|
||||
uv, ext_faces = load_ext_mesh()
|
||||
|
||||
# ④ 渲染黑线:marked = 烧进照片;curve_mask = 曲线像素
|
||||
marked = render_hairline_overlay(photo_bgr, points502, ext_faces, uv, black_texture_rgba)
|
||||
overlay = build_overlay_layer(h, w, points502, ext_faces, uv, black_texture_rgba)
|
||||
curve_mask = (overlay[:, :, 3] > 40).astype(np.uint8) * 255
|
||||
|
||||
# ①②③ ROI
|
||||
upper = forehead_upper_region(landmarks_mp, w, h)
|
||||
head = head_silhouette(parse_map)
|
||||
roi = cv2.bitwise_and(upper, head)
|
||||
|
||||
# ⑤ ROI ∩ 曲线以上 → 清理
|
||||
above = _above_curve_region(curve_mask, h, w)
|
||||
mask = cv2.bitwise_and(roi, above)
|
||||
mask = _clean_mask(mask, w)
|
||||
mask = mask_from_curve(curve_mask, landmarks_mp, parse_map)
|
||||
return marked, mask
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user