cellpose-web/frontend/train_result.html

214 lines
No EOL
8.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" data-bs-theme="auto">
<head>
<meta charset="UTF-8" />
<title>训练结果预览</title>
<!-- Bootstrap & Icons与前页一致 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
<style>
/* 背景与玻璃卡片 —— 与你其它页保持一致 */
body {
min-height: 100vh;
background:
radial-gradient(1200px 600px at 20% 10%, rgba(13, 110, 253, .15), transparent 60%),
radial-gradient(800px 400px at 80% 90%, rgba(32, 201, 151, .18), transparent 60%),
linear-gradient(180deg, #f8f9fa, #eef2f7);
}
@media (prefers-color-scheme: dark) {
body {
background:
radial-gradient(1200px 600px at 20% 10%, rgba(13, 110, 253, .25), transparent 60%),
radial-gradient(800px 400px at 80% 90%, rgba(32, 201, 151, .25), transparent 60%),
linear-gradient(180deg, #0b1020, #0e1326);
}
}
.glass-card {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
background: rgba(255, 255, 255, .65);
border: 1px solid rgba(255, 255, 255, .5);
}
[data-bs-theme="dark"] .glass-card {
background: rgba(17, 20, 34, .6);
border: 1px solid rgba(255, 255, 255, .08);
}
/* 标题行 */
.section-title {
display: flex;
align-items: center;
gap: .6rem;
margin: 0;
}
.hint {
font-size: .9rem;
opacity: .75
}
/* 画布容器:响应式 16:9可根据需要改比例 */
.chart-wrap {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
/* 最小高度,防止数据少时过扁 */
min-height: 280px;
}
/* 让 canvas 完整填充容器(不改 id仅加样式 */
#lossChart {
position: absolute;
inset: 0;
width: 100% !important;
height: 100% !important;
display: block;
}
/* “无结果”提示:沿用 id不改逻辑仅美化 */
#none-exist {
margin: 0 0 1rem 0;
}
</style>
</head>
<body>
<div class="container py-4">
<div class="glass-card rounded-4 shadow-lg p-4 p-md-5">
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2 mb-3">
<h1 class="section-title h4">
<i class="bi bi-graph-up-arrow"></i> 训练结果预览
</h1>
<div class="hint">Loss / Metric 曲线将根据你的训练日志自动绘制</div>
</div>
<!-- “无结果”占位:保持 id不改 hidden 的使用方式 -->
<p id="none-exist" class="alert alert-warning d-flex align-items-center" role="status" hidden>
<i class="bi bi-exclamation-triangle me-2"></i>
暂无可预览的数据,请先开始训练或检查日志输出。
</p>
<!-- 图表区域 -->
<div class="chart-wrap rounded-3 border">
<canvas id="lossChart"></canvas>
</div>
<!-- 底部小提示 -->
<div class="mt-3 text-secondary small">
提示:如果你使用 Chart.js建议设置 <code>responsive: true</code><code>maintainAspectRatio: false</code>
本页样式已自动保证画布自适应容器尺寸。
</div>
</div>
</div>
<!-- Bootstrap Bundle含 Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="api.js"></script>
<script type="module">
const API_RESULT = API_BASE + "status";
const params = new URLSearchParams(window.location.search);
const ID = params.get("id");
const msg = document.getElementById("none-exist");
const cava = document.getElementById("lossChart")
if (!ID) {
msg.textContent = "missing id in URL";
msg.hidden = false;
cava.hidden = true;
} else {
try {
const res = await axios.get(API_RESULT + "?id=" + encodeURIComponent(ID));
console.log(res);
const { exists, status } = res.data; // exists: boolean, status: "running" | "success" | "failed"...
const data = res.data;
if (!exists) {
msg.textContent = `任务 "${ID}" 不存在`;
msg.hidden = false;
cava.hidden = true;
}
else if (status == "running") {
msg.textContent = `任务 "${ID}" 仍在运行中,请耐心等待片刻后刷新。`;
msg.hidden = false;
cava.hidden = true;
}
else if (status == "failed") {
msg.textContent = `任务 "${ID}" 运行失败,由于:"Error: ${data.error}",请检查上传数据集是否存在问题`;
msg.hidden = false;
cava.hidden = true;
}
else {
msg.hidden = true;
cava.hidden = false;
const train_losses = data.train_losses;
const test_losses = data.test_losses;
const epochs = Array.from({ length: Math.max(train_losses.length, test_losses.length) }, (_, i) => i + 1);
const ctx = document.getElementById('lossChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: epochs,
datasets: [
{
label: 'Train Loss',
data: train_losses,
borderColor: 'blue',
fill: false,
tension: 0.2,
},
{
label: 'Test Loss',
data: test_losses,
borderColor: 'red',
fill: false,
tension: 0.2,
}
]
},
options: {
responsive: true,
interaction: { mode: 'index', intersect: false },
scales: {
x: { title: { display: true, text: 'Epoch' } },
y: { title: { display: true, text: 'Loss' } }
}
}
});
}
} catch (e) {
msg.textContent = "请求失败";
msg.hidden = false;
console.error(e);
}
}
window.downloadTif = function () {
const a = document.createElement("a");
a.href = API_DL + "?id=" + encodeURIComponent(ID);
a.download = ID; // 留空:文件名由服务端决定;可写具体名称
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
</script>
</body>
</html>