cellpose-web/frontend/run.html

353 lines
No EOL
12 KiB
HTML
Raw Permalink 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="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>运行分割</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
</head>
<body>
<!-- <div style="padding: 1rem 1rem;">
<div class="mb-3 border" style="padding: 1rem 1rem;">
<div class="input-group mb-3">
<input id="fileInput" type="file" class="form-control" multiple />
</div>
<hr>
<div>
<div style="padding-right: 50rem;">
<div class="input-group mb-3">
<span class="input-group-text">flow threshold:</span>
<input id="flow" type="text" class="form-control" placeholder="" />
</div>
<div class="input-group mb-3">
<span class="input-group-text">cellprob threshold:</span>
<input id="cellprob" type="text" class="form-control" placeholder="" />
</div>
<div class="input-group mb-3">
<span class="input-group-text">diameter:</span>
<input id="diameter" type="text" class="form-control" placeholder="" />
</div>
<label>
<select id="model" class="form-select"></select>
</label>
</div>
</div>
<br>
<div>
<button id="uploadBtn" class="btn btn-success">Upload</button>
<progress id="bar" max="100" value="0" style="width:300px;"></progress>
</div>
</div>
</div>
<br><br><br> -->
<div class="container py-4">
<div class="glass-card rounded-4 shadow-lg p-4 p-md-5">
<!-- 上传区 -->
<div class="mb-4">
<div class="d-flex align-items-center justify-content-between mb-3">
<h2 class="h5 section-title mb-0"><i class="bi bi-folder2-open me-2"></i>选择文件</h2>
<span class="hint">可多选</span>
</div>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-cloud-arrow-up"></i></span>
<input id="fileInput" type="file" class="form-control" multiple />
</div>
</div>
<hr class="my-4">
<!-- 参数区 -->
<div class="row g-3">
<div class="col-12 col-lg-8">
<div class="row g-3">
<div class="col-12 col-md-6">
<div class="input-group">
<span class="input-group-text"><i class="bi bi-speedometer2 me-1"></i>flow threshold</span>
<input id="flow" type="text" class="form-control" placeholder="" />
</div>
</div>
<div class="col-12 col-md-6">
<div class="input-group">
<span class="input-group-text"><i class="bi bi-bezier2 me-1"></i>cellprob threshold</span>
<input id="cellprob" type="text" class="form-control" placeholder="" />
</div>
</div>
<div class="col-12 col-md-6">
<div class="input-group">
<span class="input-group-text"><i class="bi bi-bounding-box-circles me-1"></i>diameter</span>
<input id="diameter" type="text" class="form-control" placeholder="" />
</div>
</div>
<div class="col-12 col-md-6">
<label class="w-100">
<div class="input-group">
<span class="input-group-text"><i class="bi bi-cpu-fill me-1"></i>model</span>
<select id="model" class="form-select"></select>
</div>
</label>
</div>
</div>
</div>
<!-- 右侧说明(可删) -->
<div class="col-12 col-lg-4">
<div class="p-3 rounded-3 border h-100">
<div class="d-flex align-items-center mb-2">
<i class="bi bi-info-circle me-2"></i>
<strong>提示</strong>
</div>
<div class="hint">
• 参数留空将使用默认值。<br>
• 数值可使用小数(如 0.4)。<br>
• 选择合适的模型以获得更稳健的分割效果。
</div>
</div>
</div>
</div>
<!-- 行动区 -->
<div class="mt-4 d-flex flex-column flex-md-row align-items-stretch gap-3">
<button id="uploadBtn" class="btn btn-success d-inline-flex align-items-center">
<i class="bi bi-upload me-2"></i> Upload
</button>
<div class="flex-grow-1">
<div class="d-flex align-items-center justify-content-between mb-1">
<span class="hint"><i class="bi bi-activity me-1"></i>进度</span>
<span class="hint" id="barText"></span><!-- 如需,你的 JS 可写入文本 -->
</div>
<progress id="bar" max="100" value="0"></progress>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"
integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.min.js"
integrity="sha384-ep+dxp/oz2RKF89ALMPGc7Z89QFa32C8Uv1A3TcEK8sMzXVysblLA3+eJWTzPJzT"
crossorigin="anonymous"></script>
<script src="api.js"></script>
<script>
const API_UPLOAD = API_BASE + "run_upload";
const API_MODEL = API_BASE + "models";
async function loadModels() {
const select = document.getElementById('model');
// 占位 & 禁用,避免用户在加载中点选
select.disabled = true;
select.innerHTML = '<option value="">加载中…</option>';
// 可选超时控制5 秒)
const controller = new AbortController();
const t = setTimeout(() => controller.abort(), 5000);
try {
const resp = await fetch(API_MODEL, {
headers: { 'Accept': 'application/json' },
signal: controller.signal
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
const list = Array.isArray(data.models) ? data.models : [];
// 记住之前的选择(如果有)
const prev = select.value;
// 重新渲染选项
select.innerHTML = '<option value="" disabled selected>请选择模型</option>';
const seen = new Set();
for (const name of list) {
if (!name || seen.has(name)) continue;
seen.add(name);
const opt = document.createElement('option');
opt.value = String(name);
opt.textContent = String(name);
select.appendChild(opt);
}
// 如果原选择仍然存在,则恢复它
if (prev && seen.has(prev)) {
select.value = prev;
}
} catch (err) {
console.error('加载模型列表失败:', err);
select.innerHTML = '<option value="">加载失败(点击重试)</option>';
// 点击下拉框时重试
select.addEventListener('click', loadModels, { once: true });
} finally {
clearTimeout(t);
select.disabled = false;
}
}
document.addEventListener('DOMContentLoaded', loadModels);
function buildUrl() {
// 获取参数
const model = document.getElementById('model')?.value;
const flow = (document.getElementById('flow')?.value || '').trim();
const cellp = (document.getElementById('cellprob')?.value || '').trim();
const diameter = (document.getElementById('diameter')?.value || '').trim();
// 用 URLSearchParams 组装查询串
const qs = new URLSearchParams({
model: model,
flow_threshold: flow,
cellprob_threshold: cellp,
diameter: diameter
});
return `${API_UPLOAD}?${qs.toString()}`;
}
document.getElementById("uploadBtn").addEventListener("click", async () => {
const input = document.getElementById("fileInput");
if (!input.files.length) return alert("请选择文件");
const fd = new FormData();
for (const f of input.files) fd.append("files", f);
const bar = document.getElementById("bar");
try {
const URL = buildUrl();
const res = await axios.post(URL, fd, {
onUploadProgress: (e) => {
if (e.total) bar.value = Math.round((e.loaded * 100) / e.total);
},
});
// alert("上传成功:" + JSON.stringify(res.data));
// 创建一个提示元素
const notice = document.createElement("div");
notice.style.position = "fixed";
notice.style.top = "20px";
notice.style.left = "50%";
notice.style.transform = "translateX(-50%)";
notice.style.padding = "10px 20px";
notice.style.background = "#4caf50";
notice.style.color = "white";
notice.style.borderRadius = "8px";
notice.style.fontSize = "16px";
notice.style.zIndex = "9999";
let seconds = 3;
notice.textContent = `上传成功!${seconds} 秒后跳转预览页面…`;
document.body.appendChild(notice);
const timer = setInterval(() => {
seconds--;
if (seconds > 0) {
notice.textContent = `上传成功!${seconds} 秒后跳转预览页面…`;
} else {
clearInterval(timer);
document.body.removeChild(notice);
window.location.href = `preview.html?id=${encodeURIComponent(res.data['id'])}`;
}
}, 1000);
} catch (e) {
alert("上传失败:" + (e.response?.data?.message || e.message));
} finally {
bar.value = 0;
}
});
</script>
<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, 0.65);
border: 1px solid rgba(255, 255, 255, 0.5);
}
[data-bs-theme="dark"] .glass-card {
background: rgba(17, 20, 34, 0.6);
border: 1px solid rgba(255, 255, 255, 0.08);
}
/* 区块与控件 */
.section-title {
font-weight: 600;
letter-spacing: .02em;
}
.input-group-text i {
opacity: .7;
}
.hint {
font-size: .875rem;
opacity: .75;
}
/* 原生 <progress> 美化(保持 id 不变) */
#bar {
width: 100%;
height: 1rem;
border: none;
border-radius: .75rem;
background-color: rgba(13,110,253,.15);
overflow: hidden;
vertical-align: middle;
}
/* Chrome / Edge / Safari */
#bar::-webkit-progress-bar {
background-color: transparent;
}
#bar::-webkit-progress-value {
background: linear-gradient(90deg, #0d6efd, #6ea8fe);
border-radius: .75rem;
}
/* Firefox */
#bar::-moz-progress-bar {
background: linear-gradient(90deg, #0d6efd, #6ea8fe);
border-radius: .75rem;
}
/* 上传按钮动效 */
#uploadBtn {
padding: .65rem 1.1rem;
border-radius: .8rem;
transition: transform .12s ease, box-shadow .12s ease;
}
#uploadBtn:hover, #uploadBtn:focus-visible {
transform: translateY(-1px);
box-shadow: 0 .75rem 1.25rem rgba(0,0,0,.12);
}
</style>
</body>
</html>