343 lines
19 KiB
HTML
343 lines
19 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>旷视五接口 — 前端接入说明</title>
|
||
<style>
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #f8f9fb; color: #1e293b; line-height: 1.6; }
|
||
.wrap { max-width: 960px; margin: 0 auto; padding: 32px 24px; }
|
||
|
||
h1 { font-size: 26px; margin-bottom: 8px; }
|
||
.subtitle { color: #64748b; font-size: 14px; margin-bottom: 32px; }
|
||
|
||
.card { background: #fff; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,.05); margin-bottom: 24px; overflow: hidden; }
|
||
.card h2 { font-size: 16px; padding: 16px 20px; border-bottom: 1px solid #f1f5f9; background: #fafbfc; }
|
||
.card-body { padding: 18px 20px; }
|
||
|
||
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid #f1f5f9; }
|
||
th { background: #f8fafc; font-weight: 700; color: #475569; font-size: 11px; text-transform: uppercase; letter-spacing: .5px; }
|
||
tr:last-child td { border-bottom: none; }
|
||
|
||
code { font-family: "SF Mono", "Fira Code", monospace; font-size: 12.5px; background: #f1f5f9; padding: 2px 6px; border-radius: 4px; color: #0f172a; }
|
||
pre { background: #1e293b; color: #e2e8f0; padding: 16px 20px; border-radius: 8px; font-family: "SF Mono", "Fira Code", monospace; font-size: 12.5px; line-height: 1.5; overflow-x: auto; }
|
||
|
||
.badge { display: inline-block; padding: 2px 10px; border-radius: 10px; font-size: 11px; font-weight: 700; }
|
||
.badge.post { background: #dbeafe; color: #1e40af; }
|
||
.badge.get { background: #d1fae5; color: #065f46; }
|
||
.badge.warn { background: #fef3c7; color: #92400e; }
|
||
|
||
.endpoint { margin-bottom: 20px; }
|
||
.endpoint .url { font-size: 15px; font-weight: 700; margin-bottom: 6px; }
|
||
.endpoint .desc { color: #64748b; font-size: 13px; margin-bottom: 10px; }
|
||
.endpoint .params { font-size: 12px; color: #94a3b8; margin-bottom: 8px; }
|
||
|
||
.link { color: #2563eb; text-decoration: none; font-size: 13px; }
|
||
.link:hover { text-decoration: underline; }
|
||
|
||
.toc { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 24px; }
|
||
.toc a { font-size: 13px; color: #2563eb; text-decoration: none; padding: 4px 12px; background: #eff6ff; border-radius: 6px; }
|
||
.toc a:hover { background: #dbeafe; }
|
||
|
||
hr { border: none; border-top: 1px solid #e2e8f0; margin: 20px 0; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
<h1>📡 旷视五接口 — 前端接入说明</h1>
|
||
<p class="subtitle">Base URL: <code>https://hair.xiangsilian.com</code> | 在线文档: <a href="/docs" class="link">/docs</a> | 在线测试页见底部</p>
|
||
|
||
<div class="toc">
|
||
<a href="#overview">通用约定</a>
|
||
<a href="#if1">接口1</a>
|
||
<a href="#if2">接口2</a>
|
||
<a href="#if3">接口3</a>
|
||
<a href="#if4">接口4</a>
|
||
<a href="#if5">接口5</a>
|
||
<a href="#if6">接口6</a>
|
||
<a href="#if7">接口7</a>
|
||
<a href="#errors">错误码</a>
|
||
<a href="#test">在线测试</a>
|
||
</div>
|
||
|
||
<!-- ======== 通用约定 ======== -->
|
||
<div class="card" id="overview">
|
||
<h2>🔧 通用约定</h2>
|
||
<div class="card-body">
|
||
<table>
|
||
<tr><td>协议</td><td>HTTPS</td></tr>
|
||
<tr><td>请求方式</td><td>全部 <span class="badge post">POST</span></td></tr>
|
||
<tr><td>编码</td><td>UTF-8</td></tr>
|
||
<tr><td>Content-Type</td><td><code>multipart/form-data</code></td></tr>
|
||
<tr><td>图片参数</td><td>三选一:<code>image_file</code>(文件上传)/ <code>image_url</code>(URL)/ <code>image_base64</code>(base64 + 前缀)</td></tr>
|
||
<tr><td>图片格式</td><td>JPG / PNG,单人正面照</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:14px;font-size:13px"><strong>统一响应结构</strong>:</p>
|
||
<pre>{
|
||
"code": 0, // 0=成功,非0=错误
|
||
"message": "success",
|
||
"request_id": "...", // 请求追踪 ID
|
||
"data": { ... } // 业务数据,各接口不同
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== 接口1 ======== -->
|
||
<div class="card" id="if1">
|
||
<h2>1. 四庭七眼测量 <span class="badge post">POST</span> <code>/api/v1/face/measure</code></h2>
|
||
<div class="card-body">
|
||
<p class="desc">上传正面照 → 返回标注好的 PNG(仅标注图层,透明底)+ 四庭七眼厘米数值 + 关键点像素坐标。</p>
|
||
|
||
<p><strong>入参</strong></p>
|
||
<table>
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>image_file</td><td>file</td><td>三选一</td><td>上传图片文件</td></tr>
|
||
<tr><td>image_url</td><td>string</td><td>三选一</td><td>图片 URL</td></tr>
|
||
<tr><td>image_base64</td><td>string</td><td>三选一</td><td>base64 字符串,需带 data:image/...;base64, 前缀</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:12px"><strong>data 字段</strong></p>
|
||
<table>
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td><code>annotated_image_url</code></td><td>string</td><td>标注 PNG URL(仅标注图层,透明底,叠加到原图上显示)</td></tr>
|
||
<tr><td><code>face_total_height_cm</code></td><td>number</td><td>全脸高度(cm)</td></tr>
|
||
<tr><td><code>four_courts</code></td><td>object</td><td>四庭:top/upper/middle/lower,各含 _cm 和 ratios</td></tr>
|
||
<tr><td><code>seven_eyes</code></td><td>object</td><td>七眼:eye_width/face_width/inter_eye_distance,各含 _cm 和 ratios</td></tr>
|
||
<tr><td><code>landmarks</code></td><td>object</td><td>5 个关键点像素坐标:hair_top/hairline/brow_center/nose_bottom/chin_tip</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:12px;font-size:12px;color:#64748b">💡 前端把标注图叠加到原图上即可呈现测量效果(标注图白色线条 #FFFFFF,透明底)。</p>
|
||
|
||
<p style="margin-top:8px"><strong>请求示例</strong>(fetch):</p>
|
||
<pre>const fd = new FormData();
|
||
fd.append('image_file', file); // 或 image_url / image_base64
|
||
|
||
const res = await fetch('https://hair.xiangsilian.com/api/v1/face/measure', {
|
||
method: 'POST',
|
||
body: fd
|
||
});
|
||
const { code, data } = await res.json();
|
||
// data.annotated_image_url → 标注 PNG
|
||
// data.face_total_height_cm → 全脸高度
|
||
// data.four_courts.ratios → { top_court: 0.25, ... }
|
||
// 前端叠加显示:原图 + annotated_image_url(absolute 叠加)</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== 接口2 ======== -->
|
||
<div class="card" id="if2">
|
||
<h2>2. C端生发 <span class="badge post">POST</span> <code>/api/v1/hair/grow</code></h2>
|
||
<div class="card-body">
|
||
<p class="desc">上传正面照 + 性别 + 发型序号 → 返回指定发际线类型的预览图与生发图。</p>
|
||
|
||
<p><strong>入参</strong></p>
|
||
<table>
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>image_file / image_url / image_base64</td><td>—</td><td>三选一</td><td>用户正面照</td></tr>
|
||
<tr><td>gender</td><td>string</td><td>✅ 必填</td><td><code>"male"</code> / <code>"female"</code></td></tr>
|
||
<tr><td>hair_style</td><td>int</td><td>✅ 必填</td><td>发型序号。female: 1~5,male: 1~4(见下方映射)</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:12px"><strong>data.results[] 元素</strong></p>
|
||
<table>
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td><code>image_url</code></td><td>string</td><td>发际线叠加预览图(曲线叠在原图上)</td></tr>
|
||
<tr><td><code>grown_image_url</code></td><td>string</td><td>生发后效果图(ComfyUI/Flux「植发3个月」)⚠ 可空</td></tr>
|
||
<tr><td><code>hairline_type</code></td><td>string</td><td>发际线类型 key</td></tr>
|
||
<tr><td><code>order</code></td><td>int</td><td>排序(1=最佳,当前按贴图顺序)</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:8px;font-size:12px;color:#64748b">
|
||
Female 5 种:ellipse/flower/heart/straight/wave |
|
||
Male 4 种:ellipse/m/straight/inverse_arc<br>
|
||
⚠ 生发图由本机 ComfyUI 生成,耗时可达数分钟,fetch 超时需放大(≥5min)。
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== 接口3 ======== -->
|
||
<div class="card" id="if3">
|
||
<h2>3. B端生发(医生端) <span class="badge post">POST</span> <code>/api/v1/hair/grow-b</code></h2>
|
||
<div class="card-body">
|
||
<p class="desc">医生用马克笔在用户照片额头画线 → 拍照上传 → 系统生成生发效果图。</p>
|
||
|
||
<p><strong>入参</strong></p>
|
||
<table>
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>marked_image_file / marked_image_url / marked_image_base64</td><td>—</td><td>✅ 三选一</td><td>已用马克笔标注发际线的图片</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:12px"><strong>data 字段</strong></p>
|
||
<table>
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td><code>hair_growth_image_url</code></td><td>string</td><td>生发后效果图 URL ⚠ 可空</td></tr>
|
||
<tr><td><code>hairline_type</code></td><td>string</td><td>固定 <code>"custom"</code>(手绘定制)</td></tr>
|
||
</table>
|
||
|
||
<p style="font-size:12px;color:#64748b">⚠ 划线图未检测到划线(或无人脸)→ code=1001</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== 接口4 ======== -->
|
||
<div class="card" id="if4">
|
||
<h2>4. 用户特征分析 <span class="badge post">POST</span> <code>/api/v1/face/features</code></h2>
|
||
<div class="card-body">
|
||
<p class="desc">上传照片 → 火山方舟豆包视觉模型分析 → 返回几十项面部特征(脸型/眉形/肤色/四季色彩…)。</p>
|
||
|
||
<p><strong>入参</strong>:image_file / image_url / image_base64 三选一。无其他参数。</p>
|
||
|
||
<p style="margin-top:12px"><strong>data 字段</strong></p>
|
||
<table>
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td><code>features</code></td><td>string</td><td><strong>JSON 字符串</strong>(不是对象!客户端需 <code>JSON.parse()</code>)</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:8px"><strong>features 英文优先字段</strong>(其余中文字段同时返回,共~42个):</p>
|
||
<table>
|
||
<tr><th>字段</th><th>说明</th><th>字段</th><th>说明</th></tr>
|
||
<tr><td>face_shape</td><td>脸型</td><td>eyebrow_shape</td><td>眉形</td></tr>
|
||
<tr><td>facial_age</td><td>面部年龄区间</td><td>gender</td><td>性别</td></tr>
|
||
<tr><td>dynamic_static_type</td><td>动静类型</td><td>gene_style</td><td>基因风格</td></tr>
|
||
</table>
|
||
|
||
<pre>const res = await fetch('https://hair.xiangsilian.com/api/v1/face/features', {
|
||
method: 'POST',
|
||
body: fd
|
||
});
|
||
const { code, data } = await res.json();
|
||
const features = JSON.parse(data.features); // ← 注意:data.features 是字符串!
|
||
console.log(features.face_shape); // "鹅蛋脸"
|
||
console.log(features['四季色彩季型']); // "冷夏型"(中文字段也保留)</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== 接口5 ======== -->
|
||
<div class="card" id="if5">
|
||
<h2>5. 发际线 PNG 生成 <span class="badge post">POST</span> <code>/api/v1/hairline/generate</code></h2>
|
||
<div class="card-body">
|
||
<p class="desc">上传正面照 + 性别 → N 张发际线叠加图 + 最佳发际线中心点坐标。</p>
|
||
|
||
<p><strong>入参</strong></p>
|
||
<table>
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>image_file / image_url / image_base64</td><td>—</td><td>三选一</td><td>用户正面照</td></tr>
|
||
<tr><td>gender</td><td>string</td><td>✅ 必填</td><td><code>"male"</code> / <code>"female"</code></td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:12px"><strong>data 字段</strong></p>
|
||
<table>
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td><code>hairline_images[]</code></td><td>object[]</td><td>发际线叠加图列表,每项含 image_url + order</td></tr>
|
||
<tr><td><code>best_hairline_center_point</code></td><td>object</td><td>最佳发际线中心点像素坐标 <code>{ x: number, y: number }</code></td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== 接口6 ======== -->
|
||
<div class="card" id="if6">
|
||
<h2>6. 四庭七眼测量 v2 <span class="badge post">POST</span> <code>/api/v1/face/measure-v2</code> <span class="badge warn">v2</span></h2>
|
||
<div class="card-body">
|
||
<p class="desc">基于<a href="#if1">接口1</a>的变体。上传正面照 → 返回标注 PNG + 三庭七眼数据。与接口1 的差异:</p>
|
||
<ul style="font-size:13px;color:#475569;margin:8px 0 12px 18px;line-height:1.8">
|
||
<li><strong>去顶庭</strong>:不画头顶横线、不返回顶庭数据,<code>face_total_height_cm</code> = 上+中+下庭</li>
|
||
<li><strong>竖线范围</strong>:发际线→下巴尖(接口1 为头顶→下巴尖)</li>
|
||
<li><strong>无头部端线</strong>:仅七眼 6 点 5 段标尺(接口1 为 8 点 7 段)</li>
|
||
</ul>
|
||
|
||
<p><strong>入参</strong></p>
|
||
<table>
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>image_file / image_url / image_base64</td><td>—</td><td>三选一</td><td>用户正面照</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:12px"><strong>data 字段</strong></p>
|
||
<table>
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td><code>annotated_image_url</code></td><td>string</td><td>标注 PNG URL(仅标注图层,透明底)</td></tr>
|
||
<tr><td><code>face_total_height_cm</code></td><td>number</td><td>面部总高度(cm)= 三庭之和</td></tr>
|
||
<tr><td><code>four_courts</code></td><td>object</td><td>三庭:upper/middle/lower,各含 _cm 和 ratios(<strong>无 top_court</strong>)</td></tr>
|
||
<tr><td><code>seven_eyes</code></td><td>object</td><td>七眼:eye_width/face_width/inter_eye_distance</td></tr>
|
||
<tr><td><code>landmarks</code></td><td>object</td><td>4 个关键点:hairline/brow_center/nose_bottom/chin_tip(<strong>无 hair_top</strong>)</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== 接口7 ======== -->
|
||
<div class="card" id="if7">
|
||
<h2>7. C端生发 v2 <span class="badge post">POST</span> <code>/api/v1/hair/grow-v2</code> <span class="badge warn">v2</span></h2>
|
||
<div class="card-body">
|
||
<p class="desc">功能与<a href="#if2">接口2</a>完全一致,仅 ComfyUI 工作流不同——使用 <code>add_hair2.json</code> 替代 <code>add_hair.json</code>(Flux-2 Klein 9b)。</p>
|
||
|
||
<p><strong>入参</strong></p>
|
||
<table>
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>image_file / image_url / image_base64</td><td>—</td><td>三选一</td><td>用户正面照</td></tr>
|
||
<tr><td>gender</td><td>string</td><td>✅ 必填</td><td><code>"male"</code> / <code>"female"</code></td></tr>
|
||
<tr><td>hair_style</td><td>int</td><td>✅ 必填</td><td>发型序号。female: 1~5,male: 1~4</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:12px"><strong>data.results[] 元素</strong>(同接口2)</p>
|
||
<table>
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td><code>image_url</code></td><td>string</td><td>发际线叠加预览图</td></tr>
|
||
<tr><td><code>grown_image_url</code></td><td>string</td><td>生发后效果图 ⚠ 可空</td></tr>
|
||
<tr><td><code>hairline_type</code></td><td>string</td><td>发际线类型 key</td></tr>
|
||
<tr><td><code>order</code></td><td>int</td><td>排序</td></tr>
|
||
</table>
|
||
|
||
<p style="margin-top:8px;font-size:12px;color:#64748b">
|
||
Female 5 种:ellipse/flower/heart/straight/wave |
|
||
Male 4 种:ellipse/m/straight/inverse_arc<br>
|
||
⚠ 工作流: add_hair2.json(Flux-2 Klein 9b),输入节点 26,输出节点 75。
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== 错误码 ======== -->
|
||
<div class="card" id="errors">
|
||
<h2>⚠ 错误码</h2>
|
||
<div class="card-body">
|
||
<table>
|
||
<tr><th>code</th><th>message</th><th>说明</th></tr>
|
||
<tr><td>1001</td><td>无法识别人像</td><td>未检测到人脸</td></tr>
|
||
|
||
<tr><td>1003</td><td>角度问题,非正面照</td><td>非正面 / 角度过大</td></tr>
|
||
<tr><td>1005</td><td>检测到多张人脸</td><td>仅支持单人</td></tr>
|
||
|
||
<tr><td>1007</td><td>图片参数错误 / 后端不可用</td><td>参数传错 / 服务繁忙请稍后重试</td></tr>
|
||
<tr><td>1008</td><td>图片格式不支持</td><td>非 JPG/PNG / base64 解码失败</td></tr>
|
||
</table>
|
||
<p style="font-size:12px;color:#94a3b8;margin-top:8px">1004 已废弃(接口2 不再自动判性别,改由客户端传 gender 参数)。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ======== 在线测试页 ======== -->
|
||
<div class="card" id="test">
|
||
<h2>🧪 在线测试页面</h2>
|
||
<div class="card-body">
|
||
<table>
|
||
<tr><th>接口</th><th>测试页</th><th>功能</th></tr>
|
||
<tr><td>1. 四庭七眼</td><td><a href="/static/test_interface1.html" class="link">/static/test_interface1.html</a></td><td>上传照片 → 原图+标注叠加,底图/标注开关,指标卡片</td></tr>
|
||
<tr><td>2. C端生发</td><td><a href="/static/test_interface2.html" class="link">/static/test_interface2.html</a></td><td>上传+性别 → 方案一覧(原图/叠加/生发),双图对比</td></tr>
|
||
<tr><td>3. B端生发</td><td><a href="/static/test_interface3.html" class="link">/static/test_interface3.html</a></td><td>划线图上传 → 生发效果图</td></tr>
|
||
<tr><td>4. 用户特征</td><td><a href="/static/test_interface4.html" class="link">/static/test_interface4.html</a></td><td>上传照片 → 42项面部特征表格 + 原始JSON</td></tr>
|
||
<tr><td>5. 发际线PNG</td><td><a href="/static/test_interface5.html" class="link">/static/test_interface5.html</a></td><td>上传+性别 → 发际线方案+中心点坐标</td></tr>
|
||
<tr><td>6. 四庭七眼 v2</td><td><a href="/static/test_interface6.html" class="link">/static/test_interface6.html</a></td><td>同接口1,去顶庭 · 竖线发际线→下巴 · 无头部端线</td></tr>
|
||
<tr><td>7. C端生发 v2</td><td><a href="/static/test_interface7.html" class="link">/static/test_interface7.html</a></td><td>同接口2,使用 add_hair2.json 工作流(Flux-2 Klein 9b)</td></tr>
|
||
</table>
|
||
<p style="font-size:12px;color:#94a3b8;margin-top:12px">
|
||
完整 API 文档:<a href="/docs" class="link">/docs</a>(Swagger UI)
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<p style="text-align:center;font-size:12px;color:#cbd5e1;margin-top:32px">
|
||
Base: https://hair.xiangsilian.com | Swagger: /docs | 接入说明: /static/integration.html
|
||
</p>
|
||
</div>
|
||
</body>
|
||
</html>
|