mirror of
https://github.com/ClovertaTheTrilobita/cellpose-web.git
synced 2026-04-01 23:14:50 +00:00
Compare commits
No commits in common. "master" and "0.9.1" have entirely different histories.
13 changed files with 182 additions and 1135 deletions
11
README.md
11
README.md
|
|
@ -6,13 +6,8 @@
|
|||
<img alt="Static Badge" src="https://img.shields.io/badge/Redis-6.4.0-red">
|
||||
<img alt="Static Badge" src="https://img.shields.io/badge/JSDelivr-in_use-brown">
|
||||
<img alt="Static Badge" src="https://img.shields.io/badge/Flask-3.1.2-8ecae6">
|
||||
<br><br>
|
||||
<img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/da5b891e-b4ac-484a-885c-0856f18e04fc" style="height: 70%; width: 70%"/>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
🌈 实现功能:
|
||||
|
|
@ -21,18 +16,16 @@
|
|||
- ⛵️ 训练、分割结果随时下载
|
||||
- 📚 权重可直接作为后续分割模型
|
||||
- 🛠️ 一键安装部署脚本
|
||||
- 🎨 前端样式美化
|
||||
- 🚧 [TODO] 前端样式美化
|
||||
|
||||
<br>
|
||||
|
||||
## 🚀一键安装
|
||||
|
||||
在<b>[最新Release页面](https://github.com/ClovertaTheTrilobita/cellpose-web/releases/latest)</b>中下载最新的 <b>install.sh</b> 到你的Linux/macOS机器上。
|
||||
在<b>[Release页面](https://github.com/ClovertaTheTrilobita/cellpose-web/releases)</b>中下载最新的 <b>install.sh</b> 到你的Linux/macOS机器上。
|
||||
|
||||
将它放到你希望项目存在的位置,并执行它,安装脚本会将项目自动拉取到同一目录下。
|
||||
|
||||
**NOTE:** 安装脚本设计上支持Debian系、Arch系、RHEL系Linux,但目前仅测试过Arch Linux。其他发行版暂未经过测试,若出现意外错误,推荐手动安装。
|
||||
|
||||
Windows暂时不支持通过脚本一键安装。
|
||||
|
||||
<br>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
backend:
|
||||
ip: 192.168.193.141
|
||||
ip: 10.10.25.240
|
||||
port: 5000
|
||||
|
||||
model:
|
||||
|
|
|
|||
|
|
@ -62,20 +62,6 @@ class Cprun:
|
|||
diameter: float | None = None,
|
||||
flow_threshold: float = 0.4,
|
||||
cellprob_threshold: float = 0.0, ):
|
||||
"""
|
||||
运行 cellpose 分割
|
||||
|
||||
Args:
|
||||
images: [list] 图片存储路径
|
||||
time: [str] 开始运行的时间,相当于本次运行的ID,用于存储运行结果
|
||||
model: [str] 图像分割所使用的模型
|
||||
diameter: [float] diameters are used to rescale the image to 30 pix cell diameter.
|
||||
flow_threshold: [float] flow error threshold (all cells with errors below threshold are kept) (not used for 3D). Defaults to 0.4.
|
||||
cellprob_threshold: [float] all pixels with value above threshold kept for masks, decrease to find more and larger masks. Defaults to 0.0.
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
if time is None:
|
||||
return [False, "No time received"]
|
||||
|
|
@ -85,10 +71,9 @@ class Cprun:
|
|||
|
||||
message = [f"Using {model} model"]
|
||||
|
||||
# 设定模型参数
|
||||
model = models.CellposeModel(gpu=True, model_type=model)
|
||||
files = images
|
||||
imgs = [imread(f) for f in files] # 获取目录中的每一个文件
|
||||
imgs = [imread(f) for f in files]
|
||||
masks, flows, styles = model.eval(
|
||||
imgs,
|
||||
flow_threshold=flow_threshold,
|
||||
|
|
@ -105,7 +90,7 @@ class Cprun:
|
|||
out = base + "_output"
|
||||
save_masks(imgs, mask, flow, out, tif=True)
|
||||
|
||||
# 用 plot 生成彩色叠加图
|
||||
# 用 plot 生成彩色叠加图(不依赖 skimage)
|
||||
rgb = plot.image_to_rgb(img, channels=[0, 0]) # 原图转 RGB
|
||||
over = plot.mask_overlay(rgb, masks=mask, colors=None) # 叠加彩色实例
|
||||
Image.fromarray(over).save(base + "_overlay.png")
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import redis
|
|||
import datetime
|
||||
import json
|
||||
|
||||
from sympy import false
|
||||
|
||||
CONFIG_PATH = Path(__file__).parent / "config.yaml"
|
||||
cfg = OmegaConf.load(CONFIG_PATH)
|
||||
cfg.data.root_dir = str((CONFIG_PATH.parent / cfg.data.root_dir).resolve())
|
||||
|
|
@ -19,19 +17,6 @@ os.environ["CELLPOSE_LOCAL_MODELS_PATH"] = MODELS_DIR
|
|||
r = redis.Redis(host="127.0.0.1", port=6379, db=0)
|
||||
|
||||
def set_status(task_id, status, train_losses, test_losses, **extra):
|
||||
"""
|
||||
修改redis数据库中某一任务的运行状态
|
||||
|
||||
Args:
|
||||
task_id: 这一任务的时间戳
|
||||
status: 任务状态
|
||||
train_losses: 此次任务的训练loss
|
||||
test_losses: 此次任务的测试loss
|
||||
**extra:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
payload = {"status": status,
|
||||
"updated_at": datetime.datetime.utcnow().isoformat(),
|
||||
"train_losses": train_losses.tolist() if hasattr(train_losses, "tolist") else train_losses,
|
||||
|
|
@ -53,47 +38,7 @@ class Cptrain:
|
|||
model_name: str | None = None,
|
||||
image_filter: str = "_img",
|
||||
mask_filter: str = "_masks",
|
||||
base_model: str = "cpsam",
|
||||
train_probs: list[float] = None,
|
||||
test_probs: list[float] = None,
|
||||
batch_size: int = 8,
|
||||
learning_rate = 5e-5,
|
||||
n_epochs: int = 100,
|
||||
weight_decay=0.1,
|
||||
normalize: bool =True,
|
||||
compute_flows: bool = False,
|
||||
min_train_masks: int = 5,
|
||||
nimg_per_epoch: int =None,
|
||||
rescale: bool= False,
|
||||
scale_range=None,
|
||||
channel_axis: int = None,
|
||||
):
|
||||
"""
|
||||
开始训练
|
||||
|
||||
Args:
|
||||
time: 此次任务的时间戳(即任务ID)
|
||||
model_name: 训练结果命名
|
||||
image_filter:
|
||||
mask_filter:
|
||||
base_model:
|
||||
train_probs:
|
||||
test_probs:
|
||||
batch_size:
|
||||
learning_rate:
|
||||
n_epochs:
|
||||
weight_decay:
|
||||
normalize:
|
||||
compute_flows:
|
||||
min_train_masks:
|
||||
nimg_per_epoch:
|
||||
rescale:
|
||||
scale_range:
|
||||
channel_axis:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
base_model: str = "cpsam"):
|
||||
|
||||
train_dir = Path(TRAIN_DIR) / time
|
||||
test_dir = Path(TEST_DIR) / time
|
||||
|
|
@ -111,13 +56,9 @@ class Cptrain:
|
|||
model_path, train_losses, test_losses = train.train_seg(model.net,
|
||||
train_data=images, train_labels=labels,
|
||||
test_data=test_images, test_labels=test_labels,
|
||||
train_probs=train_probs, test_probs=test_probs,
|
||||
weight_decay=weight_decay, learning_rate=learning_rate,
|
||||
n_epochs=n_epochs, model_name=model_name,
|
||||
save_path=BASE_DIR, batch_size=batch_size,
|
||||
normalize=normalize, compute_flows=compute_flows, min_train_masks=min_train_masks,
|
||||
nimg_per_epoch=nimg_per_epoch, rescale=rescale, scale_range=scale_range, channel_axis=channel_axis
|
||||
)
|
||||
weight_decay=0.1, learning_rate=1e-5,
|
||||
n_epochs=100, model_name=model_name,
|
||||
save_path=BASE_DIR)
|
||||
|
||||
set_status(time, "done", train_losses, test_losses)
|
||||
print("模型已保存到:", model_path)
|
||||
|
|
|
|||
|
|
@ -93,10 +93,15 @@ def run_upload():
|
|||
return default
|
||||
|
||||
flow_threshold = _to_float(request.args.get("flow_threshold") or request.form.get("flow_threshold"), 0.4)
|
||||
cellprob_threshold = _to_float(request.args.get("cellprob_threshold") or request.form.get("cellprob_threshold"),0.0)
|
||||
cellprob_threshold = _to_float(request.args.get("cellprob_threshold") or request.form.get("cellprob_threshold"),
|
||||
0.0)
|
||||
diameter_raw = request.args.get("diameter") or request.form.get("diameter")
|
||||
diameter = _to_float(diameter_raw, None) if diameter_raw not in (None, "") else None
|
||||
|
||||
print("cpt:" + str(cellprob_threshold))
|
||||
print("flow:" + str(flow_threshold))
|
||||
print("diameter:" + str(diameter))
|
||||
|
||||
# 将文件保存在本地目录中
|
||||
ts = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + f"-{int(time.time()*1000)%1000:03d}"
|
||||
os.makedirs(Path(UPLOAD_DIR) / ts, exist_ok=True)
|
||||
|
|
@ -135,69 +140,12 @@ def run_upload():
|
|||
|
||||
@app.post("/train_upload")
|
||||
def train_upload():
|
||||
"""
|
||||
从前端获取训练数据和测试数据,并开始训练
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
def _to_float(x, default):
|
||||
"""
|
||||
将变量转为float类型
|
||||
Args:
|
||||
x: 变量
|
||||
default: 默认值
|
||||
"""
|
||||
|
||||
try:
|
||||
return float(x)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
def _to_int(x, default):
|
||||
"""
|
||||
将变量转为int类型
|
||||
Args:
|
||||
x: 变量
|
||||
default: 默认值
|
||||
"""
|
||||
try:
|
||||
return int(x)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
# 获取从前端传来的参数
|
||||
ts = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + f"-{int(time.time()*1000)%1000:03d}"
|
||||
model_name = request.args.get("model_name") or f"custom_model-{ts}"
|
||||
image_filter = request.args.get("image_filter") or "_img"
|
||||
mask_filter = request.args.get("mask_filter") or "_masks"
|
||||
base_model = request.args.get("base_model") or "cpsam"
|
||||
batch_size = _to_int(request.args.get("batch_size"), 8)
|
||||
learning_rate = _to_float(request.args.get("learning_rate"), 5e-5)
|
||||
n_epochs = _to_int(request.args.get("n_epochs"), 100)
|
||||
weight_decay = _to_float(request.args.get("weight_decay"), 0.1)
|
||||
normalize = request.args.get(
|
||||
"normalize",
|
||||
default=True,
|
||||
type=lambda v: str(v).strip().lower() in ("1","true","t","yes","y","on")
|
||||
)
|
||||
compute_flows = request.args.get(
|
||||
"compute_flows",
|
||||
default=True,
|
||||
type=lambda v: str(v).strip().lower() in ("1","true","t","yes","y","on")
|
||||
)
|
||||
min_train_masks = _to_int(request.args.get(" min_train_masks"), 5)
|
||||
nimg_per_epoch = _to_int(request.args.get("nimg_per_epoch"), None)
|
||||
rescale = request.args.get(
|
||||
"rescale",
|
||||
default=False,
|
||||
type=lambda v: str(v).strip().lower() in ("1","true","t","yes","y","on")
|
||||
)
|
||||
scale_range = _to_float(request.args.get("scale_range"), None)
|
||||
channel_axis = _to_int(request.args.get("channel_axis"), None)
|
||||
|
||||
# 创建工作目录
|
||||
train_files = request.files.getlist("train_files")
|
||||
test_files = request.files.getlist("test_files")
|
||||
os.makedirs(Path(TRAIN_DIR) / ts, exist_ok=True)
|
||||
|
|
@ -219,30 +167,23 @@ def train_upload():
|
|||
saved.append(os.path.join(TEST_DIR, ts, name))
|
||||
|
||||
def job():
|
||||
"""
|
||||
子线程方法
|
||||
"""
|
||||
return asyncio.run(Cptrain.start_train(
|
||||
time=ts, model_name=model_name, image_filter=image_filter, mask_filter=mask_filter, base_model=base_model,
|
||||
batch_size=batch_size, learning_rate=learning_rate, n_epochs=n_epochs, weight_decay=weight_decay,
|
||||
normalize=normalize, compute_flows=compute_flows, min_train_masks=min_train_masks, nimg_per_epoch=nimg_per_epoch,
|
||||
rescale=rescale, scale_range=scale_range, channel_axis=channel_axis,
|
||||
time=ts,
|
||||
model_name=model_name,
|
||||
image_filter=image_filter,
|
||||
mask_filter=mask_filter,
|
||||
base_model=base_model
|
||||
))
|
||||
|
||||
# 创建一个子线程,防止阻塞主线程
|
||||
fut = executor.submit(job)
|
||||
|
||||
def done_cb(f):
|
||||
"""
|
||||
获取训练结果,并存入redis数据库
|
||||
"""
|
||||
try:
|
||||
train_losses, test_losses = f.result()
|
||||
set_train_status(ts, "success", train_losses, test_losses)
|
||||
except Exception as e:
|
||||
set_status(ts, "failed", error=str(e))
|
||||
|
||||
# 添加回调,在子线程执行完后更新redis中任务状态
|
||||
fut.add_done_callback(done_cb)
|
||||
|
||||
return jsonify({"ok": True, "count": len(saved), "id": ts})
|
||||
|
|
@ -256,20 +197,12 @@ def status():
|
|||
"""
|
||||
task_id = request.args.get('id')
|
||||
st = get_status(task_id)
|
||||
print(st)
|
||||
if not st:
|
||||
return jsonify({"ok": True, "exists": False, "status": "not_found"}), 200
|
||||
return jsonify({"ok": True, "exists": True, **st}), 200
|
||||
|
||||
@app.get("/preview")
|
||||
def preview():
|
||||
"""
|
||||
获取本次分割结果的预览
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
task_id = request.args.get('id')
|
||||
task_dir = Path(OUTPUT_DIR) / task_id
|
||||
if not task_dir.exists():
|
||||
|
|
@ -294,24 +227,11 @@ def preview():
|
|||
|
||||
@app.get("/models")
|
||||
def list_models():
|
||||
"""
|
||||
获取现有模型列表
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
models_list = os.listdir(MODELS_DIR) # 查询模型列表中有哪些文件
|
||||
models_list = os.listdir(MODELS_DIR)
|
||||
return jsonify({"ok": True, "models": models_list})
|
||||
|
||||
@app.get("/result")
|
||||
def list_results():
|
||||
"""
|
||||
获取运行结果
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
task_id = request.args.get('id')
|
||||
st = get_status(task_id)
|
||||
if not st:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from multiprocessing import Process
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 启动测试服务器
|
||||
# Cprun.run_test()
|
||||
p = Process(target=run_dev)
|
||||
p.start()
|
||||
print(f"Flask running in PID {p.pid}")
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
const config = {
|
||||
server: {
|
||||
protocol: 'http',
|
||||
host: '192.168.193.141',
|
||||
host: '10.10.25.240',
|
||||
port: 5000
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,139 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" data-bs-theme="auto">
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>任务面板</title>
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" />
|
||||
<!-- Bootstrap Icons -->
|
||||
<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, 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);
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
transition: transform .15s ease, box-shadow .15s ease;
|
||||
padding: 1.1rem 1.4rem;
|
||||
font-size: 1.05rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
.action-btn:hover, .action-btn:focus-visible {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 0.75rem 1.25rem rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: .6rem;
|
||||
}
|
||||
|
||||
.brand-badge {
|
||||
letter-spacing: .08em;
|
||||
background: rgba(13,110,253,.1);
|
||||
color: #0d6efd;
|
||||
border: 1px solid rgba(13,110,253,.15);
|
||||
}
|
||||
[data-bs-theme="dark"] .brand-badge {
|
||||
background: rgba(13,110,253,.15);
|
||||
color: #9ec5fe;
|
||||
border-color: rgba(13,110,253,.25);
|
||||
}
|
||||
</style>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Bootstrap demo</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">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="container min-vh-100 d-flex align-items-center justify-content-center py-5">
|
||||
<div class="col-12 col-md-10 col-lg-8">
|
||||
<div class="glass-card rounded-4 shadow-lg p-4 p-md-5 text-center">
|
||||
<div class="d-flex justify-content-center mb-3">
|
||||
<span class="badge brand-badge rounded-pill px-3 py-2">
|
||||
<i class="bi bi-rocket-takeoff me-1"></i> Cell Processing with <a href="https://github.com/MouseLand/cellpose">CELLPOSE</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1 class="display-6 fw-semibold mb-2">欢迎使用任务面板</h1>
|
||||
<p class="text-secondary mb-4">
|
||||
选择你要进行的操作。可随时返回此页切换任务。
|
||||
</p>
|
||||
|
||||
<div class="row g-3 g-md-4 justify-content-center">
|
||||
<div class="col-12 col-sm-6">
|
||||
<a href="run.html" class="btn btn-primary w-100 action-btn btn-icon" role="button" aria-label="进入运行页面">
|
||||
<i class="bi bi-play-circle-fill fs-4"></i>
|
||||
<span>
|
||||
运行<br>
|
||||
<small class="opacity-75">快速执行已有模型/流程</small>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6">
|
||||
<a href="train.html" class="btn btn-outline-primary w-100 action-btn btn-icon" role="button" aria-label="进入训练页面">
|
||||
<i class="bi bi-cpu-fill fs-4"></i>
|
||||
<span>
|
||||
训练<br>
|
||||
<small class="opacity-75">启动或继续训练任务</small>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4 my-md-5">
|
||||
|
||||
<div class="d-flex flex-column flex-md-row gap-2 justify-content-center">
|
||||
<a href="https://github.com/ClovertaTheTrilobita/cellpose-web" class="link-secondary text-decoration-none">
|
||||
<i class="bi bi-github"></i> Github
|
||||
</a>
|
||||
<span class="text-secondary d-none d-md-inline">•</span>
|
||||
<a href="#" class="link-secondary text-decoration-none">
|
||||
<i class="bi bi-gear"></i> 设置
|
||||
</a>
|
||||
<span class="text-secondary d-none d-md-inline">•</span>
|
||||
<span class="text-secondary"><i class="bi bi-keyboard"></i> 快捷键:<kbd>R</kbd> 运行,<kbd>T</kbd> 训练</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 键盘快捷键(可选) -->
|
||||
<script>
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (['INPUT','TEXTAREA'].includes((e.target.tagName || '').toUpperCase())) return;
|
||||
if (e.key.toLowerCase() === 'r') location.href = 'run.html';
|
||||
if (e.key.toLowerCase() === 't') location.href = 'train.html';
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- 只需引入 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>
|
||||
<a href="run.html">运行</a>
|
||||
<a href="train.html">训练</a>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,148 +1,9 @@
|
|||
<!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, 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);
|
||||
}
|
||||
|
||||
/* 标题区 */
|
||||
.page-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .6rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.sub-hint {
|
||||
font-size: .9rem;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
/* 预览网格:仅美化 #gallery,保持 id 不变 */
|
||||
#gallery {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
/* 预览卡片 */
|
||||
.thumb {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 1rem;
|
||||
background: rgba(0,0,0,.05);
|
||||
border: 1px solid rgba(0,0,0,.06);
|
||||
transition: transform .12s ease, box-shadow .12s ease;
|
||||
}
|
||||
.thumb:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 .9rem 1.2rem rgba(0,0,0,.12);
|
||||
}
|
||||
.thumb img, .thumb canvas, .thumb video {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
.thumb .badge-overlay {
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
left: .5rem;
|
||||
padding: .25rem .5rem;
|
||||
border-radius: .5rem;
|
||||
background: rgba(13,110,253,.85);
|
||||
color: #fff;
|
||||
font-size: .75rem;
|
||||
}
|
||||
|
||||
/* 空态提示:仅美化 #none-exist */
|
||||
#none-exist {
|
||||
margin: 0;
|
||||
padding: 1rem 1.25rem;
|
||||
border-radius: .75rem;
|
||||
background: rgba(108,117,125,.08);
|
||||
border: 1px dashed rgba(108,117,125,.35);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* 下载按钮 */
|
||||
.download-wrap {
|
||||
gap: .75rem;
|
||||
}
|
||||
.download-btn {
|
||||
padding: .65rem 1.1rem;
|
||||
border-radius: .8rem;
|
||||
transition: transform .12s ease, box-shadow .12s ease;
|
||||
}
|
||||
.download-btn:hover, .download-btn:focus-visible {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 .75rem 1.25rem rgba(0,0,0,.12);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container py-4">
|
||||
<div class="glass-card rounded-4 shadow-lg p-4 p-md-5">
|
||||
<!-- 标题区(保持语义,未改 id) -->
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2 mb-3">
|
||||
<h1 class="page-title h4">
|
||||
<i class="bi bi-images"></i> 运行结果预览
|
||||
</h1>
|
||||
<div class="sub-hint">
|
||||
生成的图像/掩膜将显示在下方网格中
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空态提示:仅样式增强,不改 id/hidden 机制 -->
|
||||
<h1>运行结果预览</h1>
|
||||
<p id="none-exist" hidden></p>
|
||||
|
||||
<!-- 预览网格:只美化 #gallery -->
|
||||
<div id="gallery"></div>
|
||||
|
||||
<!-- 下载按钮:保留 onclick,不动逻辑,只加样式 -->
|
||||
<div class="mt-4 d-flex download-wrap">
|
||||
<button class="btn btn-primary d-inline-flex align-items-center download-btn"
|
||||
onclick="downloadTif()">
|
||||
<i class="bi bi-download me-2"></i> 下载 TIF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 仅需引入 bundle(如你已有可省略) -->
|
||||
<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>
|
||||
|
||||
<button onclick="downloadTif()">下载tif</button>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
<script src="api.js"></script>
|
||||
|
|
@ -206,5 +67,3 @@
|
|||
document.body.removeChild(a);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>运行分割</title>
|
||||
<title>Bootstrap demo</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 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 />
|
||||
|
|
@ -48,94 +47,7 @@
|
|||
</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>
|
||||
<br><br><br>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
|
|
@ -272,82 +184,6 @@
|
|||
}
|
||||
});
|
||||
</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>
|
||||
|
|
@ -1,290 +1,58 @@
|
|||
<!doctype html>
|
||||
<html lang="zh-CN" data-bs-theme="auto">
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>训练任务</title>
|
||||
|
||||
<!-- Bootstrap 5.3 与 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-bottom: 0;
|
||||
}
|
||||
.hint { font-size: .9rem; opacity: .75 }
|
||||
|
||||
/* 小卡片(参数分组) */
|
||||
.subcard {
|
||||
background: rgba(255,255,255,.6);
|
||||
border: 1px solid rgba(0,0,0,.05);
|
||||
border-radius: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
[data-bs-theme="dark"] .subcard {
|
||||
background: rgba(255,255,255,.04);
|
||||
border-color: rgba(255,255,255,.08);
|
||||
}
|
||||
|
||||
/* input-group 统一图标透明度 */
|
||||
.input-group-text i { opacity: .8; }
|
||||
|
||||
/* progress 美化(保持 id 不变) */
|
||||
#bar {
|
||||
width: 100%;
|
||||
height: 1rem;
|
||||
border: none;
|
||||
border-radius: .75rem;
|
||||
background-color: rgba(13,110,253,.15);
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#bar::-webkit-progress-bar { background: transparent; }
|
||||
#bar::-webkit-progress-value {
|
||||
background: linear-gradient(90deg, #0d6efd, #6ea8fe);
|
||||
border-radius: .75rem;
|
||||
}
|
||||
#bar::-moz-progress-bar {
|
||||
background: linear-gradient(90deg, #0d6efd, #6ea8fe);
|
||||
border-radius: .75rem;
|
||||
}
|
||||
|
||||
/* 上传/启动按钮动效(保持 id 不变) */
|
||||
#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);
|
||||
}
|
||||
|
||||
/* 更紧凑的行距 */
|
||||
.g-tight { row-gap: .75rem; }
|
||||
</style>
|
||||
<title>Bootstrap demo</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">
|
||||
</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-cpu"></i> 训练配置
|
||||
</h1>
|
||||
<div class="hint">选择训练/测试数据并设置模型参数</div>
|
||||
</div>
|
||||
|
||||
<!-- 选择文件 -->
|
||||
<div class="mb-4">
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-lg-6">
|
||||
<label class="form-label fw-semibold">
|
||||
<i class="bi bi-folder2-open me-1"></i>选择训练文件
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-cloud-arrow-up"></i></span>
|
||||
<div style="padding: 1rem 1rem;">
|
||||
<div class="mb-3 border" style="padding: 1rem 1rem;">
|
||||
<div class="input-group mb-3">
|
||||
<h5>选择训练文件</h5>
|
||||
<input id="trainFileInput" type="file" class="form-control" multiple />
|
||||
</div>
|
||||
<div class="form-text">可多选;支持拖拽到输入框。</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-6">
|
||||
<label class="form-label fw-semibold">
|
||||
<i class="bi bi-folder2 me-1"></i>选择测试文件
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-cloud-arrow-up"></i></span>
|
||||
<div class="input-group mb-3">
|
||||
<h5>选择测试文件</h5>
|
||||
<input id="testFileInput" type="file" class="form-control" multiple />
|
||||
</div>
|
||||
<div class="form-text">可选;用于评估训练效果。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<!-- 参数区 -->
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-lg-8">
|
||||
<!-- 基础设置 -->
|
||||
<div class="subcard mb-3">
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<i class="bi bi-sliders2-vertical"></i><strong>基础设置</strong>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 g-tight">
|
||||
<div class="col-12">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-type me-1"></i>model name</span>
|
||||
<hr>
|
||||
<div>
|
||||
<div style="padding-right: 50rem;">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">model name:</span>
|
||||
<input id="name" 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-image me-1"></i>image filter</span>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">image filter:</span>
|
||||
<input id="image_filter" type="text" class="form-control" placeholder="_img" />
|
||||
</div>
|
||||
<div class="form-text">如 <code>*_img.png</code></div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-brush me-1"></i>masks filter</span>
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-text">masks filter</span>
|
||||
<input id="masks_filter" type="text" class="form-control" placeholder="_masks" />
|
||||
</div>
|
||||
<div class="form-text">如 <code>*_masks.png</code></div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="w-100">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-cpu-fill me-1"></i>model</span>
|
||||
<label>
|
||||
<select id="model" class="form-select"></select>
|
||||
</div>
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 训练超参 -->
|
||||
<div class="subcard">
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<i class="bi bi-rocket-takeoff"></i><strong>训练超参数</strong>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 g-tight">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-speedometer2 me-1"></i>learning rate</span>
|
||||
<input id="learning_rate" type="text" class="form-control" placeholder="0.005" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-hash me-1"></i>n epochs</span>
|
||||
<input id="n_epochs" type="text" class="form-control" placeholder="100" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-diagram-2 me-1"></i>weight decay</span>
|
||||
<input id="weight_decay" type="text" class="form-control" placeholder="0.1" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-arrows-move me-1"></i>normalize</span>
|
||||
<select id="normalize" class="form-select">
|
||||
<option selected value="1">True</option>
|
||||
<option value="0">False</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-gear me-1"></i>compute flows</span>
|
||||
<!-- 注意:此处 id 仍为 normalize(你原文如此)。建议后续改名避免重复 -->
|
||||
<select id="compute_flows" class="form-select">
|
||||
<option selected value="0">False</option>
|
||||
<option value="1">True</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-text">与光流/流场相关,通常仅在特定任务启用。</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-123 me-1"></i>min train masks</span>
|
||||
<input id="min_train_masks" type="text" class="form-control" placeholder="5" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-collection me-1"></i>nimg_per_epoch</span>
|
||||
<input id="nimg_per_epoch" 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-layers me-1"></i>channel axis</span>
|
||||
<input id="channel_axis" type="text" class="form-control" placeholder="" />
|
||||
<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>
|
||||
</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>
|
||||
• filter 用于匹配文件名,如 <code>*_img.png</code>、<code>*_masks.png</code>。<br>
|
||||
• 选择合适的预训练模型可加速收敛。<br>
|
||||
• 建议仅微调必要超参,保持可复现性。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 行动与进度(保持 id 与机制不变) -->
|
||||
<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-play-circle 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>
|
||||
</div>
|
||||
<progress id="bar" max="100" value="0"></progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 只需引入 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>
|
||||
<br><br><br>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
|
|
@ -358,29 +126,13 @@
|
|||
const model_name = (document.getElementById('model_name')?.value || '').trim();
|
||||
const image_filter = (document.getElementById('image_filter')?.value || '').trim();
|
||||
const masks_filter = (document.getElementById('masks_filter')?.value || '').trim();
|
||||
const learning_rate = (document.getElementById('learning_rate')?.value || '').trim();
|
||||
const n_epochs = (document.getElementById('n_epochs')?.value || '').trim();
|
||||
const weight_decay = (document.getElementById('weight_decay')?.value || '').trim();
|
||||
const normalize = document.getElementById('normalize')?.value;
|
||||
const compute_flows = document.getElementById('compute_flows')?.value;
|
||||
const min_train_masks = (document.getElementById('min_train_masks')?.value || '').trim();
|
||||
const nimg_per_epoch = (document.getElementById('nimg_per_epoch')?.value || '').trim();
|
||||
const channel_axis = (document.getElementById('weight_decay')?.value || '').trim();
|
||||
|
||||
// 用 URLSearchParams 组装查询串
|
||||
const qs = new URLSearchParams({
|
||||
model: model,
|
||||
model_name: model_name,
|
||||
image_filter: image_filter,
|
||||
masks_filter: masks_filter,
|
||||
learning_rate: learning_rate,
|
||||
n_epochs: n_epochs,
|
||||
weight_decay: weight_decay,
|
||||
normalize: normalize,
|
||||
compute_flows: compute_flows,
|
||||
min_train_masks: min_train_masks,
|
||||
nimg_per_epoch: nimg_per_epoch,
|
||||
channel_axis: channel_axis
|
||||
masks_filter: masks_filter
|
||||
});
|
||||
|
||||
return `${API_UPLOAD}?${qs.toString()}`;
|
||||
|
|
|
|||
|
|
@ -1,118 +1,8 @@
|
|||
<!doctype html>
|
||||
<html lang="zh-CN" data-bs-theme="auto">
|
||||
|
||||
<head>
|
||||
<!DOCTYPE html>
|
||||
<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">
|
||||
<h1>训练结果预览</h1>
|
||||
<p id="none-exist" hidden></p>
|
||||
<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>
|
||||
|
|
@ -138,17 +28,12 @@
|
|||
const data = res.data;
|
||||
|
||||
if (!exists) {
|
||||
msg.textContent = `任务 "${ID}" 不存在`;
|
||||
msg.textContent = `id "${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.textContent = `id "${ID}" 仍在运行中,请耐心等待片刻后刷新。`;
|
||||
msg.hidden = false;
|
||||
cava.hidden = true;
|
||||
}
|
||||
|
|
@ -209,6 +94,3 @@
|
|||
document.body.removeChild(a);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -434,6 +434,6 @@ else
|
|||
fi
|
||||
|
||||
echo -e "${GREEN}==>${RESET} Deployment successfull"
|
||||
if ask_yn "Do you wish to start cellpose developmental server now?" y; then
|
||||
if ask_yn "Do you wish to start cellpose server now?" y; then
|
||||
python ${root}/backend/main.py
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in a new issue