Files
hair/static/test_interface72.html
T

129 lines
5.1 KiB
HTML
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.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>72 号接口 · B 端五个关键点</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; background: #eef2f5; color: #192d3d; font: 16px/1.55 system-ui, sans-serif; }
main { width: min(1040px, 100%); margin: auto; padding: 26px 18px; }
h1 { margin: 0; font-size: clamp(24px, 4vw, 35px); }
h2 { margin: 0 0 10px; font-size: 19px; }
p { margin: 8px 0; }
.note { color: #4f6577; }
.controls, .panel { background: white; border-radius: 12px; padding: 18px; margin-top: 16px; }
form { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
button { background: #156a72; color: white; border: 0; border-radius: 7px; padding: 9px 15px; font: inherit; cursor: pointer; }
button:disabled { opacity: .55; cursor: wait; }
input { max-width: 100%; }
#status[data-error="true"] { color: #a22629; }
.results { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 16px; }
[hidden] { display: none; }
.photo { display: grid; border-radius: 8px; overflow: hidden; background: #1b2c37; }
.photo img { grid-area: 1 / 1; width: 100%; display: block; }
.panel a { color: #156a72; }
table { width: 100%; border-collapse: collapse; margin: 8px 0 20px; font-variant-numeric: tabular-nums; }
th, td { text-align: left; border-bottom: 1px solid #e6edf2; padding: 5px 7px; }
@media(max-width: 680px) { .results { grid-template-columns: 1fr; gap: 0; } }
</style>
</head>
<body>
<main>
<h1>72 号接口 · B 端五个关键点</h1>
<p class="note">上传正面照,查看扶正裁切后的六个白点、四条虚线和厘米标注。</p>
<section class="controls">
<form id="form">
<label for="file">人脸照片</label>
<input id="file" type="file" accept="image/jpeg,image/png" required>
<button id="submit" type="submit">生成标注</button>
<button id="sample" type="button">测试样例</button>
</form>
<p id="status" role="status">请选择 JPG 或 PNG 图片。</p>
</section>
<div id="results" class="results" hidden>
<section class="panel">
<h2>标注预览</h2>
<div class="photo">
<img id="base" alt="扶正裁切后的底图">
<img id="overlay" alt="白点、虚线及距离标注">
</div>
<p><a id="download" download="interface72-overlay.png">下载透明标注层</a></p>
</section>
<section class="panel">
<h2>测量结果</h2>
<table><thead><tr><th>点位</th><th>距离</th></tr></thead><tbody id="distances"></tbody></table>
<h2>坐标(像素)</h2>
<table><thead><tr><th>点号</th><th>X</th><th>Y</th></tr></thead><tbody id="points"></tbody></table>
<p class="note">点 6(MediaPipe 446)是射线锚点。六个白点的直径相同。</p>
</section>
</div>
</main>
<script>
const byId = (id) => document.getElementById(id);
const status = byId("status");
const results = byId("results");
const submit = byId("submit");
const sample = byId("sample");
function setRows(id, rows) {
const body = byId(id);
body.replaceChildren();
for (const values of rows) {
const row = body.insertRow();
for (const value of values) row.insertCell().textContent = value;
}
}
function imageSource(data, field, mime) {
return data[field + "_url"] || ("data:" + mime + ";base64," + data[field + "_base64"]);
}
async function runAnnotation(loadFile) {
submit.disabled = sample.disabled = true;
results.hidden = true;
status.dataset.error = "false";
status.textContent = "正在扶正裁切并生成标注…";
try {
const file = await loadFile();
const form = new FormData();
form.append("image_file", file, file.name || "sample.jpg");
const response = await fetch("/api/v1/face/b-end-points", { method: "POST", body: form });
const { code, message, data } = await response.json();
if (!response.ok || code !== 0) throw new Error(message || "请求失败");
byId("base").src = imageSource(data, "input_image", "image/jpeg");
const overlay = imageSource(data, "annotated_image", "image/png");
byId("overlay").src = overlay;
byId("download").href = overlay;
setRows("distances", Object.entries(data.measurements_cm).map(([key, value]) => [
key, value.toFixed(1) + " cm"
]));
setRows("points", Object.entries(data.points).map(([key, point]) => [
key, point.x.toFixed(1), point.y.toFixed(1)
]));
results.hidden = false;
status.textContent = "标注完成。";
} catch (error) {
status.dataset.error = "true";
status.textContent = error.message;
} finally {
submit.disabled = sample.disabled = false;
}
}
byId("form").addEventListener("submit", (event) => {
event.preventDefault();
const file = byId("file").files[0];
if (file) runAnnotation(() => file);
});
sample.addEventListener("click", () => runAnnotation(async () => {
const response = await fetch("/static/sample.jpg");
if (!response.ok) throw new Error("无法读取样例图片");
return response.blob();
}));
</script>
</body>
</html>