feat: 完善分割工作区导入导出与管理流程

- 新增基于 JWT 当前用户的登录恢复、角色权限、用户管理、审计日志和演示出厂重置后台接口与前端管理页。

- 重串 GT_label 导出和 GT Mask 导入逻辑:导出保留类别真实 maskid,导入仅接受灰度或 RGB 等通道 maskid 图,支持未知 maskid 策略、尺寸最近邻拉伸和导入预览。

- 统一分割结果导出体验:默认当前帧,按项目抽帧顺序和 XhXXmXXsXXXms 时间戳命名 ZIP 与图片,补齐 GT/Pro/Mix/分开 Mask 输出和映射 JSON。

- 调整工作区左侧工具栏:移除创建点/线段入口,新增画笔、橡皮擦及尺寸控制,并按绘制、布尔、导入/AI 工具分组分隔。

- 扩展 Canvas 编辑能力:画笔按语义分类绘制并可自动并入连通选中 mask,橡皮擦对选中区域扣除,优化布尔操作、选区、撤销重做和保存状态联动。

- 优化自动传播时间轴显示:同一蓝色系按传播新旧递进变暗,老传播记录达到阈值后统一旧记录色,并维护范围选择与清空后的历史显示。

- 将 AI 智能分割入口替换为更明确的 AI 元素图标,并同步侧栏、工作区和 AI 页面入口表现。

- 完善模板分类、maskid 工具函数、分类树联动、遮罩透明度、边缘平滑和传播链同步相关前端状态。

- 扩展后端项目、媒体、任务、Dashboard、模板和传播 runner 的用户隔离、任务控制、进度事件与兼容处理。

- 补充前后端测试,覆盖用户管理、GT_label 往返导入导出、GT Mask 校验和预览、画笔/橡皮擦、时间轴传播历史、导出范围、WebSocket 与 API 封装。

- 更新 AGENTS、README 和 doc 文档,记录当前接口契约、实现状态、测试计划、安装说明和 maskid/GT_label 规则。
This commit is contained in:
2026-05-03 03:52:32 +08:00
parent 4c1d3dba73
commit afcddfaeb9
62 changed files with 6572 additions and 849 deletions

View File

@@ -39,19 +39,19 @@ Word 方案描述的理想系统包含:
| DICOM 批量导入 | 部分落地 | 上传和解析存在,项目级体验还需完善 |
| WebSocket 进度 | 已落地 | 拆帧进度写入任务表后发布到 Redis `seg:progress`FastAPI 广播到 `/ws/progress` |
| SAM 推理 | 部分落地 | 当前产品入口启用 SAM 2.1 tiny/small/base+/large 和真实 GPU/SAM2.1 状态接口SAM 2.1 已接 point/box/interactive 和 video predictor 片段传播。SAM 3 桥接源码保留,但前端入口和后端 registry 已禁用 |
| 模板库 | 部分落地 | 分类、颜色、z-index 能存储和编辑PNG mask 导出时会按 zIndex 做语义融合裁决,前端预览裁决尚未落地 |
| 标注持久化 | 部分落地 | 后端有 `Annotation`前端已接入新增、回显、分类更新、当前帧删除、手工绘制、GT mask 导入、seed point 编辑、polygon 顶点拖动/删除、边中点插点和多 polygon 子区域编辑;复杂洞结构编辑未落地 |
| COCO / Mask 导出 | 已落地基础能力 | `backend/routers/export.py`COCO JSON PNG mask ZIP 前端按钮均已接入,ZIP 包含单标注 mask、语义融合 mask 和类别映射 |
| 模板库 | 部分落地 | 分类、颜色、maskid 和拖拽排序能存储和编辑;右侧语义分类树也可拖拽调整内部覆盖顺序PNG mask 导出时会按内部优先级做语义融合裁决,前端预览裁决尚未落地 |
| 标注持久化 | 部分落地 | 后端有 `Annotation` 表,前端已接入新增、回显、分类更新、传播链前后帧同目标同步换类、当前帧删除、手工绘制、GT mask 导入、seed point 编辑、polygon 顶点拖动/删除、边中点插点和多 polygon 子区域编辑;复杂洞结构编辑未落地 |
| COCO / Mask 导出 | 已落地基础能力 | `backend/routers/export.py`COCO JSON、兼容 PNG mask ZIP 和统一分割结果 ZIP 均已接入;统一 ZIP 包含 maskid/GT 像素值映射、原始图片、按帧/类别合并的分开 mask、GT_label 黑白图、Pro_label 彩色图和 Mix_label 原图叠加图GT_label 像素值使用类别真实 maskid缺失 maskid 的旧标注才补下一个可用正整数 |
## 当前代码尚未落地的目标
- SAM 3`sam3_engine.py``sam3_external_worker.py``setup_sam3_env.sh` 作为历史实现保留;由于当前系统不给文本提示,前端不再展示 SAM 3后端 registry 也不暴露 `sam3`。官方没有 SAM 3 tiny/small 权重,当前可选最小真实 SAM 权重仍是 SAM 2.1 tiny。
- Celery 异步任务队列:已注册 Celery app 和拆帧 worker task`/api/media/parse` 会创建任务表记录并入队。
- GT mask 导入:当前已支持二值/多类别 mask 导入,后端会按非零像素值拆分区域,生成 polygon 标注和距离变换 seed point骨架提取、HDBSCAN 和模板自动映射尚未实现。
- GT mask 导入:当前已支持二值 mask、灰度/16-bit GT_label 图和 RGB 三通道完全相同的 `[X,X,X]` maskid 图导入,后端会按 maskid 拆分区域,生成 polygon 标注和距离变换 seed point超出现有类别的 maskid 可舍弃或导入为未定义类别;普通彩色类别图会被拒绝,尺寸不一致会自动最近邻拉伸到当前帧;骨架提取、HDBSCAN 和更复杂的模板自动映射尚未实现。
- Mask 到点区域的拓扑降维:当前完成 distance transform seed point 和前端 seed point 拖拽编辑骨架提取、HDBSCAN 等增强尚未实现。
- 类别优先级融合PNG mask 导出时已按 zIndex 生成语义融合 mask前端裁决预览尚未实现。
- 类别优先级融合PNG mask 导出时已按内部优先级生成语义融合 mask前端裁决预览尚未实现。
- 撤销/重做:当前已有全局 mask 历史栈。
- 结构化归档保存:工作区按钮已调用 `POST /api/ai/annotate` 保存当前未归档 mask通过 `PATCH /api/ai/annotations/{id}` 更新 dirty mask。
- 保存状态按钮:工作区按钮按待保存数量显示“保存 X 个改动”或“已全部保存”,并调用 `POST /api/ai/annotate` 保存当前未归档 mask通过 `PATCH /api/ai/annotations/{id}` 更新 dirty mask。
## 结论

View File

@@ -37,6 +37,7 @@
| `workspace` | `VideoWorkspace` | 分割工作区 |
| `ai` | `AISegmentation` | AI 智能分割页 |
| `templates` | `TemplateRegistry` | 模板库 |
| `admin` | `UserAdmin` | 管理员用户后台,仅 `role=admin` 可见 |
未登录时,`App.tsx` 直接渲染 `Login`
@@ -44,14 +45,14 @@
全局状态在 `src/store/useStore.ts` 中,主要包括:
- 登录状态:`isAuthenticated``token`
- 登录状态:`isAuthenticated``token``currentUser`
- 项目:`projects``currentProject`
- 工作区:`activeModule``activeTool``frames``currentFrameIndex`
- 标注与 mask`annotations``masks`
- 模板:`templates``activeTemplateId`
- UI`isLoading``error`
当前状态管理是前端内存状态,没有持久化到 localStorage,除了登录 token
当前状态管理主要是前端内存状态;登录 token 会持久化到 `localStorage`,刷新后再通过 `/api/auth/me` 恢复当前用户
## 数据流
@@ -59,8 +60,18 @@
1. `Login.tsx` 调用 `login()`
2. `src/lib/api.ts` 请求 `POST /api/auth/login`
3. FastAPI `backend/routers/auth.py` 校验 `admin / 123456`
4. 前端把返回 token 写入 localStorage。
3. FastAPI `backend/routers/auth.py` 查询 `users` 表并校验密码哈希
4. 前端把返回 JWT 写入 localStorage,并把用户资料写入 store
5. 后续业务请求带 `Authorization: Bearer <token>`,后端按当前用户过滤项目资源。
6. `admin/annotator` 可调用写入类业务接口,`viewer` 只能读取;`/api/admin/*` 仅允许 `admin`
### 管理员用户管理
1. `Sidebar.tsx` 仅对 `currentUser.role === 'admin'` 显示“用户管理”。
2. `UserAdmin.tsx` 调用 `GET/POST/PATCH/DELETE /api/admin/users` 完成用户新增、停用/启用、角色修改、改密码和删除无项目用户。
3. `UserAdmin.tsx` 调用 `GET /api/admin/audit-logs` 展示登录成功/失败以及用户管理操作审计。
4. `UserAdmin.tsx` 危险区“恢复演示出厂设置”需要浏览器确认和输入 `RESET_DEMO_FACTORY`,随后调用 `POST /api/admin/demo-factory-reset`
5. 后端 `backend/routers/admin.py` 会阻止管理员删除、停用或降级自己,并阻止删除仍拥有项目的用户;演示出厂重置会清空其它用户、项目帧、标注、任务和私有模板,重新创建一个尚未生成帧的 `Data_MyVideo_1` 视频项目。
### 项目与拆帧
@@ -100,6 +111,6 @@
- 前端 API/WS 地址虽然已支持环境变量和 hostname 推导,但部署时仍需要确认浏览器可访问 `:8000` 后端。
- AI 当前启用 SAM 2.1 tiny/small/base+/large 点/框/interactive 路径;语义文本提示和 SAM 3 产品入口已禁用,`model=sam3` 会被后端拒绝。SAM 3 源码保留但不计入当前可用功能。
- 工作区顶部“导出 JSON 标注集”“导出 PNG Mask ZIP”“导入 GT Mask”和“结构化归档保存”已接入导出、GT 多类别导入、seed point 回显/编辑、标注新增和 dirty 标注更新清空当前帧遮罩会删除对应后端标注。手工绘制、polygon 顶点拖动/删除、区域合并/去除和撤销重做已经落到前端 mask 数据结构。
- 工作区顶部“分割结果导出”和保存状态按钮、左侧工具栏“导入 GT Mask”已接入统一导出、GT 多类别导入、seed point 回显/编辑、标注新增和 dirty 标注更新;导入 GT Mask 支持二值 mask、低数值/16-bit GT_label 图和 RGB 三通道完全相同的 `[X,X,X]` maskid 图,未知 maskid 可由用户选择舍弃或导入为未定义类别,普通彩色类别图会被拒绝,尺寸不同会自动最近邻拉伸到当前帧。保存状态按钮会按待保存数量显示“保存 X 个改动”或“已全部保存”;统一导出可选择整体视频、特定范围帧或当前图片,并勾选分开 mask、GT_label 黑白图、Pro_label 彩色图和 Mix_label 原图叠加图;特定范围帧导出支持直接输入起止帧,也支持在播放进度条或视频处理进度条上点击/拖拽选择范围Mix_label 支持默认 0.3 的透明度调节和首帧预览;后端统一导出 ZIP 固定包含 maskid/GT 像素值映射 JSON 与原始图片文件夹GT_label 像素值使用类别真实 maskid缺失 maskid 的旧标注才补下一个可用正整数,并按客户命名规则输出分开 Mask、GT_label、Pro_label 和 Mix_label 文件夹;清空当前帧遮罩会删除对应后端标注。手工绘制、polygon 顶点拖动/删除、区域合并/去除和撤销重做已经落到前端 mask 数据结构。
- Dashboard 初始统计、队列和活动日志来自后端聚合接口;解析队列来自 `processing_tasks`worker 进度通过 Redis `seg:progress` 转发到 WebSocket。任务取消、重试和失败详情已接入前后端。
- 后端路由大多未做真实鉴权
- 后端已接入 Bearer JWT 鉴权、当前用户项目隔离和角色权限;写入类业务接口要求 `admin/annotator`,管理员用户后台要求 `admin`。当前审计覆盖登录和用户管理操作,全业务级审计仍可继续扩展

View File

@@ -12,7 +12,7 @@
| 元素 | 位置 | 状态 | 说明 |
|------|------|------|------|
| 登录拦截 | `App.tsx` | 真实可用 | 未登录显示 `Login`,登录后显示主界面 |
| 模块切换 | `Sidebar.tsx` + `App.tsx` | 真实可用 | 切换 `dashboard/projects/workspace/ai/templates` |
| 模块切换 | `Sidebar.tsx` + `App.tsx` | 真实可用 | 切换 `dashboard/projects/workspace/ai/templates`“AI智能分割”入口使用 Bot + Sparkles 组合图标,强化 AI 语义 |
| Logo | `Sidebar.tsx` | 真实可用 | 使用 `/logo.png`,文件存在于 `public/logo.png` |
| GPU 状态圆标 | `Sidebar.tsx` | 真实可用 | 通过 `GET /api/ai/models/status` 显示 GPU/CPU 和当前模型可用性 |
@@ -20,16 +20,29 @@
| 元素 | 状态 | 说明 |
|------|------|------|
| 用户名/密码输入 | 真实可用 | 默认填入 `admin / 123456` |
| 安全登录按钮 | 真实可用 | 调用 `POST /api/auth/login` |
| 用户名/密码输入 | 真实可用 | 默认填入 `admin / 123456`,用户名使用 `autocomplete=username`,密码使用 `autocomplete=current-password` |
| 安全登录按钮 | 真实可用 | 调用 `POST /api/auth/login`,后端校验 `users` 表密码哈希并返回签名 JWT |
| 错误提示 | 真实可用 | 捕获后端错误并显示 |
| 安全审计说明文字 | Mock / UI-only | UI 文案,没有真实审计功能 |
| 登录态恢复 / 退出 | 真实可用 | 页面刷新后用 `/api/auth/me` 恢复当前用户;侧栏底部显示当前用户名并可退出登录 |
| 安全审计说明文字 | 部分可用 | 登录和用户管理操作已有 `audit_logs` 记录;登录页“端到端加密”等安全文案仍是展示性说明,不代表已接入完整企业级安全审计 |
## 管理员用户后台
| 元素 | 状态 | 说明 |
|------|------|------|
| 侧栏“用户管理”入口 | 真实可用 | 仅当前用户 `role=admin` 时显示;非管理员无法看到入口,后端 `/api/admin/*` 也会返回 403 |
| 用户列表 | 真实可用 | 调用 `GET /api/admin/users`,展示用户 id、用户名、角色、启停用状态和创建时间 |
| 新增用户 | 真实可用 | 调用 `POST /api/admin/users`,支持设置用户名、初始密码和 `admin/annotator/viewer` 角色;后端校验用户名唯一和密码长度 |
| 修改角色 / 启停用 / 改密码 | 真实可用 | 调用 `PATCH /api/admin/users/{id}`;后端禁止管理员把自己降级或停用,避免锁死后台 |
| 删除用户 | 真实可用 | 调用 `DELETE /api/admin/users/{id}`;后端禁止删除自己,且用户名下仍有项目时返回 409避免悬空项目数据 |
| 审计日志 | 真实可用 | 调用 `GET /api/admin/audit-logs`,展示登录成功/失败、用户新增、修改和删除等管理操作 |
| 恢复演示出厂设置 | 真实可用 | 管理员点击危险区按钮后先浏览器确认,再输入 `RESET_DEMO_FACTORY`;前端调用 `POST /api/admin/demo-factory-reset`,后端只保留默认 admin 与一个尚未生成帧的演示视频项目,并清空用户、项目帧、标注、任务和私有模板等演示数据 |
## Dashboard 系统概况
| 元素 | 状态 | 说明 |
|------|------|------|
| WebSocket 连接状态 | 真实可用 | 前端通过 `src/lib/config.ts` 推导或读取 `VITE_WS_PROGRESS_URL`,后端有 `/ws/progress` |
| WebSocket 连接状态 | 真实可用 | 前端通过 `src/lib/config.ts` 推导或读取 `VITE_WS_PROGRESS_URL`,后端有 `/ws/progress`Dashboard 卸载或切页导致的主动断开不会触发自动重连也不会继续输出“Connection closed”噪音 |
| 任务进度 | 真实可用 | 初始数据来自 `GET /api/dashboard/overview`,按 `processing_tasks` queued/running/success/failed/cancelled 任务生成;统计卡片中的处理中任务数只计算 queued/running |
| 任务取消 | 真实可用 | queued/running 任务显示取消按钮,调用 `POST /api/tasks/{task_id}/cancel` |
| 任务重试 | 真实可用 | failed/cancelled 任务显示重试按钮,调用 `POST /api/tasks/{task_id}/retry` 创建新任务 |
@@ -65,12 +78,11 @@
| 无帧项目提示 | 真实可用 | 如果 `video_path` 存在但无帧,只提示回到项目库生成帧,不自动创建拆帧任务 |
| SAM 模型状态徽标 | 真实可用 | 工作区顶栏使用紧凑 GPU/CPU 状态徽标,避免和旁边的“传播权重”下拉重复显示 SAM 2.1 变体名称;悬停仍可查看模型状态说明 |
| 已保存标注回显 | 真实可用 | 加载工作区帧后调用 `GET /api/ai/annotations` 并渲染已保存 mask回显时保留当前项目帧里尚未保存的 AI/手工 draft mask避免从 AI 页推送的候选被覆盖 |
| “导出 JSON 标注集”按钮 | 真实可用 | 导出前会保存未归档 mask然后调用 `exportCoco()` 下载 JSON |
| “导出 PNG Mask ZIP”按钮 | 真实可用 | 导出前会保存未归档 mask后调用 `GET /api/export/{project_id}/masks` 下载 ZIP后端同时包含单标注 mask、每帧语义融合 mask 和 `semantic_classes.json` |
| “导入 GT Mask”按钮 | 真实可用 | 选择图片后调用 `POST /api/ai/import-gt-mask`,后端按非零像素值和连通域生成 polygon 标注与距离变换 seed point再回显到工作区 |
| 参考帧/起止帧/传播权重/自动传播 | 真实可用 | 当前打开帧即参考帧,前端会使用该帧全部 mask 作为 seed工作区顶栏有独立“传播权重”下拉可在传播前二次选择 SAM 2.1 tiny/small/base+/large 权重,不提供 SAM2/SAM3 家族切换,不影响 AI 智能分割页的单帧推理权重选择;传播权重下拉使用深色背景和青色文字,避免默认灰底白字不可读;如果用户尚未显式设置范围,点击“自动传播”会先进入时间轴范围选择模式,播放进度条视频处理进度条都可点击/拖拽回填传播起始帧和传播结束帧,再点击“开始传播”提交;用户也可直接改数字框后点击按钮传播。提交后前端把传播权重 id、seed mask、seed 来源 id、未编辑传播结果的原始 seed 签名、mask 边缘平滑参数和前/后方向步骤提交到 `POST /api/ai/propagate/task`,后端先规范化/校验权重 id再创建 `processing_tasks` 并由 Celery 执行对应 SAM 2.1 video predictorworker 会在本次目标帧段内按 seed 来源和几何/语义/平滑参数签名做幂等判断,未改变且目标帧已有结果的 seed 直接跳过,已改变、目标帧只部分覆盖、换权重或平滑参数变化时会先删除本次目标帧段内同源旧自动传播标注再重新传播;中间帧人工新增/修改同一物体后重新传播时,后端会按语义和目标帧空间重叠清理旧传播结果,写入前清理不受旧结果 `propagation_direction` 限制,避免 backward 重传时与旧 forward mask 重叠;传播中顶栏显示任务进度、已处理帧次、删除旧区域数和已保存区域数,前端轮询 `GET /api/tasks/{task_id}` 并刷新已保存标注;任务可取消,若完成后 0 个新区域会明确提示没有生成新 mask 或已跳过未改变 mask |
| 清空片段遮罩 | 真实可用 | 点击“清空片段遮罩”后会进入和自动传播一致的时间轴范围选择模式,用户可在播放进度条或视频处理进度条上点击/拖拽选择起止帧,再点“确认清空”;执行后删除该帧段内所有本地 draft mask并对已保存 mask 调用 `DELETE /api/ai/annotations/{annotation_id}` 删除后端标注;不在范围内的 mask 和选区会保留 |
| “结构化归档保存”按钮 | 真实可用 | 未保存 mask 写入 `POST /api/ai/annotate`dirty mask 写入 `PATCH /api/ai/annotations/{id}`;保存成功后会重新拉取后端标注,并用 saved annotation 替换本次提交的 draft mask避免仍显示未保存 |
| “分割结果导出”按钮 | 真实可用 | 原“导出 JSON 标注集”和“导出 PNG Mask ZIP”已合并为一个入口点击后可选择整体视频、特定范围帧或当前图片默认导出范围为当前图片并勾选导出分开二值 mask、GT_label 黑白图、Pro_label 彩色图和 Mix_label 原图叠加图;选择“特定范围帧”后会进入和自动传播、清空遮罩一致的时间轴范围选择模式,可在播放进度条或视频处理进度条上点击/拖拽选择导出起止帧,也可直接修改起止帧输入框;选择 Mix_label 时可调透明度,默认 0.3,并显示当前/待导出第一帧预览;提交前会保存未归档 mask然后调用 `GET /api/export/{project_id}/results` 下载 ZIP浏览器下载名和后端 `Content-Disposition` 均使用 `{项目库项目名}_seg_T_{起始时间戳}-{结束时间戳}_P_{起始项目帧序号}-{结束项目帧序号}.zip`;时间戳格式为 `0h00m00s000ms`,帧序号来自项目抽帧后的 1-based 顺序,不使用原视频帧号;包内固定包含 `annotations_coco.json``maskid_GT像素值_类别映射.json``原始图片/`;选择分开 mask 时包含按帧子目录组织且同类合并的 `分开Mask分割结果/`,选择 GT_label/Pro_label/Mix_label 时分别包含 `GT_label图/``Pro_label彩色分割结果/``Mix_label重叠覆盖彩色分割结果/`。GT_label 图中背景为 0语义类别值使用类别真实 maskid缺失 maskid 的旧标注才补下一个可用正整数 |
| “导入 GT Mask”按钮 | 真实可用 | 入口已从工作区顶栏移动到左侧工具栏“重叠区域去除”之后,使用紫色图标底色;选择图片后先弹出导入结果预览和未知 maskid 策略选择,可舍弃未知类别或导入为未定义类别;随后调用 `POST /api/ai/import-gt-mask`,后端支持二值 mask、低数值/16-bit GT_label 图和 RGB 三通道完全相同的 `[X,X,X]` maskid 图,不符合灰度/maskid 图要求时返回错误;尺寸不同会自动最近邻拉伸到当前帧,再按类别/连通域生成 polygon 标注与距离变换 seed point最后回显到工作区 |
| 参考帧/起止帧/传播权重/自动传播 | 真实可用 | 当前打开帧即参考帧,前端会使用该帧全部 mask 作为 seed工作区顶栏有独立“传播权重”下拉可在传播前二次选择 SAM 2.1 tiny/small/base+/large 权重,不提供 SAM2/SAM3 家族切换,不影响 AI 智能分割页的单帧推理权重选择;传播权重下拉使用深色背景和青色文字,避免默认灰底白字不可读;如果用户尚未显式设置范围,点击“自动传播”会先进入时间轴范围选择模式,播放进度条和视频处理进度条都可点击/拖拽回填传播起始帧和传播结束帧,再点击“开始传播”提交;用户也可直接改数字框后点击按钮传播。提交后前端把传播权重 id、seed mask、seed 来源 id、未编辑传播结果的原始 seed 签名和前/后方向步骤提交到 `POST /api/ai/propagate/task`,后端先规范化/校验权重 id再创建 `processing_tasks` 并由 Celery 执行对应 SAM 2.1 video predictorworker 会在本次目标帧段内按 seed 来源和几何/语义签名做幂等判断,未改变且目标帧已有结果的 seed 直接跳过,已改变、目标帧只部分覆盖或换权重时会先删除本次目标帧段内同源旧自动传播标注再重新传播;历史或外部 seed 若仍带边缘平滑参数,后端仍按完整签名兼容处理;当前前端平滑应用会直接改写 polygon因此传播以新几何参与签名中间帧人工新增/修改同一物体后重新传播时,后端会按语义和目标帧空间重叠清理旧传播结果,写入前清理不受旧结果 `propagation_direction` 限制,避免 backward 重传时与旧 forward mask 重叠;传播中顶栏显示任务进度、已处理帧次、删除旧区域数和已保存区域数,前端轮询 `GET /api/tasks/{task_id}` 并刷新已保存标注;任务可取消,若完成后 0 个新区域会明确提示没有生成新 mask 或已跳过未改变 mask |
| 清空片段遮罩 | 真实可用 | 点击“清空片段遮罩”后会进入和自动传播一致的时间轴范围选择模式,用户可在播放进度条视频处理进度条点击/拖拽选择起止帧;顶栏提供“清空全部”和“保留人工/AI”两种模式默认清空全部以保持旧行为“清空全部”会删除该帧段内所有本地 draft mask并对已保存 mask 调用 `DELETE /api/ai/annotations/{annotation_id}`,若范围内存在人工绘制或 AI 智能分割生成的红色“人工/AI 标注帧”会先弹出确认;“保留人工/AI”只删除自动传播/推理 mask不弹出人工帧确认人工/AI 标注帧、范围外 mask 和未被清空的选区会保留;同时按清空范围裁剪当前会话的自动传播历史条,避免已清空片段仍显示最近传播进度 |
| 保存状态按钮 | 真实可用 | 顶栏按钮按当前项目待保存数量显示为“保存 X 个改动”或“已全部保存”;未保存 mask 写入 `POST /api/ai/annotate`dirty mask 写入 `PATCH /api/ai/annotations/{id}`;保存成功后会重新拉取后端标注,并用 saved annotation 替换本次提交的 draft mask避免仍显示未保存 |
## CanvasArea 画布
@@ -83,14 +95,14 @@
| 正向/反向选点 | 真实可用 | UI 能加点,并按当前帧 `frame.id` 调用 `/api/ai/predict`;结果需点击归档保存才持久化 |
| 框选 | 真实可用 | UI 能画框,并把框坐标归一化后调用后端推理;结果需点击归档保存才持久化 |
| AI 推理中提示 | 真实可用 | 请求期间会显示 |
| 手工多边形/矩形/圆/点/线 | 真实可用 | 多边形点击取点后可按 Enter 完成,也可在三点后点击首节点闭合;矩形/圆/线拖拽生成 polygon点工具生成小区域;绘制工具可在已有 mask 上继续落点;均写入 `Mask.segmentation`,可归档保存 |
| 画布上下文提示 | 真实可用 | 切换到多边形、矩形、圆、线、点、正/反向选点、框选、区域合并/去除、调整多边形等隐性操作工具时,画布左上角显示当前工具的完成/取消/选择顺序提示;提示会在数秒后自动隐藏,避免长期遮挡待编辑图像,工具或操作状态变化时会重新出现 |
| Mask 渲染 | 真实可用 | 前端会把推理、手工绘制、GT 导入和已保存标注转成 Konva `pathData` 渲染 |
| Mask 透明度 | 真实可用 | 右侧语义分类树上方的“遮罩透明度”滑杆写入全局 `maskPreviewOpacity`Canvas 使用该值调整所有工作区 mask 预览透明度,选中 mask 会在该基础上略微加亮 |
| 手工多边形/矩形/圆/画笔/橡皮擦 | 真实可用 | 多边形点击取点后可按 Enter 完成,也可在三点后点击首节点闭合;矩形/圆拖拽生成 polygon画笔按当前语义分类生成连续圆形笔触并在松开时 union 成 mask若与选中 mask 连通则自动合并;橡皮擦从选中 mask 中扣除笔触区域;均写入 `Mask.segmentation`,可归档保存 |
| 画布上下文提示 | 真实可用 | 切换到多边形、矩形、圆、画笔、橡皮擦、区域合并/去除、调整多边形等隐性操作工具时,画布左上角显示当前工具的完成/取消/选择顺序提示;提示会在数秒后自动隐藏,避免长期遮挡待编辑图像,工具或操作状态变化时会重新出现 |
| Mask 渲染 | 真实可用 | 前端会把推理、手工绘制、GT 导入和已保存标注转成 Konva `pathData` 渲染;未选中特定 mask 时,当前帧 mask 会按右侧“语义分类树”拖拽得到的内部覆盖优先级从低到高渲染,使高优先级类别显示在上层;有选中 mask 时保留编辑态置顶行为,方便操作 |
| Mask 透明度 | 真实可用 | 右侧语义分类树上方的“遮罩透明度”滑杆写入全局 `maskPreviewOpacity`工作区 Canvas 和 AI 智能分割页都会使用该值调整 mask 预览透明度,选中 mask 会在该基础上略微加亮 |
| 传播链跨帧选区跟随 | 真实可用 | 用户选中某个 mask 后切到同一自动传播结果覆盖的其他帧时,`CanvasArea` 会根据 `source_annotation_id``source_mask_id``propagation_seed_key` 查找目标帧对应传播 mask 并自动选中;找不到同链结果时才清空选区 |
| Polygon 逐点编辑 / 删除 | 真实可用 | 点击 mask 后显示 polygon 顶点;按住顶点即可直接拖动并实时重算 `pathData/segmentation/bbox/area`,不需要先单击选中顶点,已保存 mask 标为 dirty顶点拖拽结束不会触发 Stage 平移Canvas 当前缩放和位置保持不变;选中顶点后 Delete/Backspace 可删点但保留至少三点;选中 mask 但未选中顶点时 Delete/Backspace 删除整个 mask已保存 mask 会同步调用后端删除 |
| GT seed point 回显/编辑 | 真实可用 | 已保存标注的 `points` 会显示为黄色 seed 点;拖动后标记为 dirty归档保存会更新后端 |
| 应用分类 | 真实可用 | Canvas 右下角按钮可将当前选择的模板分类应用到本帧 mask右侧语义分类树点击分类时会优先改当前已选 mask并把已选 mask 移到前端渲染最上层方便继续编辑;已保存 mask 会标为 dirty归档保存时更新后端 |
| 应用分类 | 真实可用 | Canvas 右下角按钮可将当前选择的模板分类应用到本帧 mask并同步同一传播链前后帧的对应 mask右侧语义分类树点击分类时会优先改当前已选 mask通过 `source_annotation_id``source_mask_id``propagation_seed_key` 同步更新同一传播链上的前后传播 mask同时把已选 mask 移到前端渲染最上层方便继续编辑;已保存 mask 会标为 dirty归档保存时更新后端 |
| 清空遮罩 | 真实可用 | 工作区中会删除当前帧已保存标注并清空当前帧本地 mask |
| 保存状态计数 | 真实可用 | 底部显示已保存、未保存、待更新数量 |
| 当前图层信息 | 真实可用 | 根据当前选中 mask 显示真实标签/后端 annotation id未保存 mask 显示“未保存”,未选中时显示“未选择” |
@@ -99,13 +111,16 @@
| 元素 | 状态 | 说明 |
|------|------|------|
| 工具分组分隔线 | 真实可用 | 拖拽/选择到创建圆为绘制/基础编辑组,画笔/橡皮擦/区域合并/重叠区域去除为局部修补与布尔编辑组,导入 GT Mask 和 AI 智能分割为外部动作组;组间使用浅灰横线分隔,便于快速扫视 |
| 拖拽/选择 | 真实可用 | 控制 Canvas 是否可拖拽 |
| 调整多边形 | 真实可用 | 选中 polygon mask 后显示顶点和边中点;支持按住顶点直接拖动、点击边中点插点、双击边界按位置插点 |
| 多边形/矩形/圆/点/线 | 真实可用 | 切换 activeTool 后由 `CanvasArea` 生成可保存的 polygon mask |
| 多边形/矩形/圆/画笔/橡皮擦 | 真实可用 | 切换 activeTool 后由 `CanvasArea` 生成或编辑可保存的 polygon mask;画笔/橡皮擦在工具栏显示尺寸滑杆 |
| 区域合并/去除 | 真实可用 | 选择工具后点击多个 mask右下角显示已选数量和操作按钮合并/去除模式会隐藏 polygon 编辑手柄,避免手柄抢占多选点击;布尔选择态中第一个选中的主区域用黄色实线轮廓,后续参与合并/扣除的区域用红色虚线轮廓,避免主区域和扣除区域看起来像随机阴影差异;使用 `polygon-clipping` 做 union / difference合并会保留主 mask 并移除被合并 mask去除会从主 mask 扣除后续选中 mask内含扣除会保留 hole ring 并用 even-odd 规则渲染 |
| 正向选点/反向选点/框选 | 部分可用 | 会影响 Canvas 交互,并能触发已对齐的 AI 推理接口;点击工作区内已有 SAM 提示点会优先删除该提示点并重新推理,不会冒泡成新增提示点或 mask 选择 |
| 导入 GT Mask | 真实可用 | 位于“重叠区域去除”之后,点击后打开文件选择器,并在上传前选择未知类别处理策略;该入口不切换 activeTool |
| 魔法棒 SAM 触发 | 部分可用 | 切到 AI 页面;不是直接执行推理 |
| 撤销/重做 | 真实可用 | 绑定 Zustand `maskHistory/maskFuture`支持工作区顶栏按钮、工具栏按钮、AI 页按钮和快捷键 `Ctrl/Cmd+Z``Ctrl/Cmd+Shift+Z``Ctrl/Cmd+Y`;输入框聚焦时不拦截快捷键 |
| AI 正向选点/反向选点/框选 | 不在工作区工具栏显示 | 这些是 AI 智能分割页功能,工作区左侧工具栏不再提供正向选点、反向选点和边界框选按钮 |
| AI 智能分割入口 | 真实可用 | 位于工作区工具栏底部,使用和侧栏一致的 Bot + Sparkles 组合图标;点击后切到 AI 智能分割页 |
| 撤销/重做 | 真实可用 | 绑定 Zustand `maskHistory/maskFuture`,工作区只保留顶栏按钮和快捷键 `Ctrl/Cmd+Z``Ctrl/Cmd+Shift+Z``Ctrl/Cmd+Y`AI 页保留自己的按钮;左侧工具栏不再重复放置撤销/重做;输入框聚焦时不拦截快捷键;工作区顶栏撤销图标使用琥珀色、重做图标使用蓝紫色,提高深色顶栏里的识别度 |
| 紧凑/滚动布局 | 真实可用 | 工具按钮使用较紧凑的垂直间距;左侧高度不足时工具栏自身出现纵向滚动,不挤压画布;外层工具栏扩展到 56px按钮列仍固定 48px滚动条占用右侧外扩空间不挤占图标位置滚动条使用 `seg-scrollbar`默认低对比融入深色工具区hover/focus 时才增强为青色提示 |
## FrameTimeline 时间轴
@@ -116,7 +131,7 @@
| 点击缩略图跳帧 | 真实可用 | 调用 `setCurrentFrame(idx)`;非当前帧中,人工/AI 标注帧使用红色边框,自动传播/推理帧使用蓝色边框;同一帧同时有人工/AI 标注和自动传播结果时,红色标注边框优先保留,蓝色传播状态以内描边表达;当前帧仍用青色外框高亮优先,若当前帧同时是人工/AI 标注帧,则在青色外框内增加红色内描边,固定为外层当前帧、内层人工/AI 标注,避免状态颜色互相覆盖 |
| 顶部 range 拖动 | 真实可用 | 改变当前帧 |
| 具体时间显示 | 真实可用 | 根据项目 `parse_fps/original_fps` 显示当前时间和总时长,格式为 `mm:ss.cc` |
| 播放进度条 / 视频处理进度条 | 真实可用 | 播放进度条位于上方,视频处理进度条位于下方;视频处理进度条普通状态下可点击跳转到对应帧;根据已保存标注回显的 `mask_data.source` / `propagated_from_frame_id` 识别自动传播生成的帧并显示蓝色区段,人工绘制或 AI 智能分割生成的帧显示红色竖线,红/蓝标识也可点击跳转到对应帧;每次自动传播成功处理帧后,工作区会在当前会话记录最近传播范围,并在视频处理进度条上叠加不同色系的深到浅渐变片段,辅助识别最近处理过的视频区间;未处理背景使用中性灰以和红/蓝/渐变标记区分;工作区进入自动传播或清空片段遮罩的范围选择模式时,两条进度条显示 amber 选区,并可点击/拖拽选择起止帧 |
| 播放进度条 / 视频处理进度条 | 真实可用 | 播放进度条位于上方,视频处理进度条位于下方;当前帧位置用一条白色竖线贯穿两条进度条,避免和青色播放进度、红/蓝处理状态混淆;视频处理进度条普通状态下可点击跳转到对应帧;根据已保存标注回显的 `mask_data.source``propagated_from_frame_id``source_annotation_id``source_mask_id``propagation_seed_key` 识别自动传播生成的帧并显示蓝色区段,人工绘制或 AI 智能分割生成的帧显示红色竖线,红/蓝标识也可点击跳转到对应帧;每次自动传播成功处理帧后,工作区会在当前会话记录最近传播范围,并在视频处理进度条上叠加同一蓝色系的纯色片段,按距最新传播的时间顺序逐次变暗,且第 5 次及更早统一为阈值旧记录色,辅助识别第一次、第二次、第 N 次传播;清空片段遮罩会同步移除或裁剪与清空范围重叠的传播历史片段;未处理背景使用中性灰以和红/蓝/传播历史标记区分;工作区进入自动传播或清空片段遮罩的范围选择模式时,两条进度条显示 amber 选区,并额外用洋红色起始线和黄绿色结束线贯穿两条进度条,表示待处理起止帧,颜色避开附近的青色、红色、蓝色和 amber 元素 |
| 播放/暂停 | 真实可用 | 当前代码按 `parse_fps/original_fps` 推进帧,最多 30fps |
| 方向键切帧 | 真实可用 | 全局监听左右方向键切到上一帧/下一帧;焦点在 input、textarea、select 或 contentEditable 内时不会拦截 |
@@ -127,12 +142,12 @@
| 模板选择 | 部分可用 | 读取全局 templates可切换 activeTemplateId |
| 面板滚动条 | 真实可用 | 右侧本体/语义分类面板内容过长时自身滚动;滚动条使用 `seg-scrollbar`默认低对比融入深色侧栏hover/focus 时才增强显示 |
| 面板标题 | 已简化 | 原“本体论与属性分类管理树”固定说明栏已移除,右侧面板直接展示模板、透明度和语义分类树 |
| 分类树展示 / 换标签 | 真实可用 | 显示当前模板 classes点击分类会设为后续新 mask 的 activeClass如果 Canvas 已选 mask则同步更新已选 mask 的标签、颜色和 class 元数据,并把已选 mask 移到前端渲染最上层;当用户在 Canvas 点击已有 mask 时,本面板会按 mask 的 class id / 名称自动切换模板、设置 active class并滚动/聚焦到对应分类按钮 |
| 分类树展示 / 换标签 | 真实可用 | 显示当前模板 classes点击分类会设为后续新 mask 的 activeClass如果 Canvas 已选 mask则同步更新已选 mask 及同一传播链前后帧对应 mask 的标签、颜色和 class 元数据,并把已选 mask 移到前端渲染最上层;当用户在 Canvas 点击已有 mask 时,本面板会按 mask 的 class id / 名称自动切换模板、设置 active class并滚动/聚焦到对应分类按钮 |
| 添加自定义分类 | 真实可用 | 需要先选择模板;新增分类通过 `PATCH /api/templates/{id}` 写入后端模板 `mapping_rules.classes`,并同步全局模板 store |
| 目标实例属性标题 | 真实可用 | “特定目标实例属性追踪”下方显示当前选中 mask 的 `className/label`,不再跟随全局 active class避免点过其他分类后标题固定成旧分类 |
| 后端拓扑锚点数量 | 真实可用 | 选中 mask 后调用 `POST /api/ai/analyze-mask`,由后端根据 seed points 或 polygon 顶点采样返回锚点数量 |
| 重新提取拓扑锚点按钮 | 真实可用 | 调用 `POST /api/ai/analyze-mask` 并带 `extract_skeleton=true`,刷新后端几何锚点统计 |
| 边缘平滑强度 / 应用边缘平滑 | 真实可用 | 选中 mask 后调整 0-100 平滑强度调用 `POST /api/ai/smooth-mask`;后端用 Chaikin polygon smoothing 返回新 polygon、bbox、面积和拓扑锚点前端把当前 mask 标记为 dirty/draft保存后 `geometry_smoothing` 写入标注 metadata自动传播 seed 会携带同一参数,前/后传播结果保存前应用一致平滑 |
| 当前选中区域计数 | 已移除 | 当前交互以单选 mask 为主,计数长期为 1属于低价值信息已从实例属性面板删除 |
| 后端拓扑锚点数量 | 真实可用 | 选中 mask 后调用 `POST /api/ai/analyze-mask`,后端按 polygon 的真实顶点数量返回 `topology_anchor_count``topology_anchors` 列表只保留最多 64 个抽样点用于调试展示,避免把真实数量误压成十几个;前端会忽略被浏览器中止或已过期的分析请求,避免切换 mask、拖动平滑预览或卸载组件时出现误报 |
| 边缘平滑强度 / 应用边缘平滑 | 真实可用 | 选中 mask 后调整 0-100 平滑强度会先即时更新滑杆数值,再在用户停止拖动约 220ms 后调用 `POST /api/ai/smooth-mask` 生成预览 polygon避免拖动时连续请求导致卡顿预览会临时替换当前 mask 显示但不标 dirty点“应用边缘平滑”后会把平滑 polygon 作为新的实际 mask 几何写入当前 mask 和同传播链前/后对应 mask整次应用进入同一个撤销/重做历史步骤,并把相关 mask 标记为 dirty/draft传播链上的 mask 保存时会保留原传播 lineage metadata不会因为平滑几何同步而在时间轴上变成人工/AI 红色标注帧;应用后平滑强度重置为 0后续可继续用“调整多边形”编辑新的 polygon后端平滑使用缓入强度曲线低强度只做温和切角和轻量去噪高强度才逐步增加 Chaikin 迭代、切角比例和简化阈值,避免 20% 前后已经过度平滑 |
## AISegmentation 独立 AI 页
@@ -145,9 +160,9 @@
| SAM 3 入口 | 当前禁用 | 因当前系统不提供文本提示,前端不再显示 SAM 3 模型选择、文本输入或 SAM 3 框选入口;后端 `model=sam3` 返回不支持 |
| 语义文本输入 | 当前禁用 | AI 页不再提供文本语义输入;后端收到 `semantic` prompt 会返回 400 |
| 参数开关 | 真实可用 | UI 展示为“局部专注模式(自动裁剪无锚区域)”和“严格除杂模式(自动清理干涉点)”,只是为了让用户更容易理解,不重命名内部字段;`cropMode` 会随 `/api/ai/predict` 发送 `crop_to_prompt`,后端对点/框 prompt 裁剪推理区域并回映射 polygon`autoDeleteBg` 会发送 `auto_filter_background``min_score`,后端过滤低分结果和覆盖负向点的结果 |
| 遮罩清晰度 | 真实可用 | 调节 AI 页候选 mask 的预览透明度,只影响本页显示,不改变 mask 几何、分类或保存数据 |
| AI 遮罩透明度 | 真实可用 | 调节共享的 `maskPreviewOpacity`AI 页候选 mask 和右侧“遮罩透明度”滑杆联动,只影响预览显示,不改变 mask 几何、分类或保存数据 |
| 执行高精度语义分割 | 真实可用 | 使用当前项目帧和所选 SAM 2.1 变体调用 `/api/ai/predict`SAM 2.1 需要点/框提示且只采用最高分候选AI 页只渲染本页最新候选,不显示工作区已有 mask重复执行会替换上一次 AI 页候选而不是叠加;生成结果写入全局 masks 并自动选中,右侧分类树可立即换标签 |
| 推送至工作区编辑 | 真实可用 | 切回工作区并把工具切到“调整多边形”,保留 AI 页选中的未保存 mask 和当前帧视角;工作区回显后端标注时不会覆盖这类 draft mask也不会强制跳回第一帧 |
| 推送至工作区编辑 | 真实可用 | 切回工作区并把工具切到“调整多边形”,保留 AI 页选中的未保存 mask 和当前帧视角;推送前会校验当前 AI 候选 mask 必须已有 `classId``className`,未选择语义分类时会用右上角 error toast 提示用户先点右侧语义分类树,不允许进入工作区;如果用户直接离开 AI 页,未分类 AI 候选会被清理,避免无语义 mask 进入工作区;工作区回显后端标注时不会覆盖这类 draft mask也不会强制跳回第一帧 |
| 撤销/重做 | 真实可用 | 绑定全局 mask 历史栈 |
| 删除最近锚点 | 真实可用 | 删除 AI 页最近一次放置的正/反向提示点,不影响已生成候选 mask 或工作区 mask |
| 删除选中候选 | 真实可用 | 删除 AI 页当前选中的本页候选 mask不会删除工作区已有 maskDelete/Backspace 也遵循同一范围 |
@@ -164,13 +179,13 @@
| 编辑模板 | 真实可用 | 调用 `PATCH /api/templates/{id}` |
| 删除模板 | 真实可用 | 调用 `DELETE /api/templates/{id}` |
| 添加/删除分类 | 真实可用 | 保存在模板 `mapping_rules.classes` |
| 拖拽排序 | 真实可用 | 重算 zIndex保存时写后端 |
| 拖拽排序 | 真实可用 | 模板库和工作区右侧语义分类树都可拖拽调整内部覆盖优先级,保存时写后端;工作区拖拽会同步当前同类 mask 的 `classZIndex` 并标记待保存;界面只显示类别稳定 maskidmaskid 不作为排序规范 |
| JSON 批量导入 | 部分可用 | 前端解析 JSON 并加入编辑态,保存后才落库 |
| 载入腹腔镜 35 分类 | 真实可用 | 前端内置数据;后端也 seed 默认模板 |
| mapping rules | 部分可用 | 可存 `rules`,但当前没有运行时映射执行引擎;适合后续用于导入外部标签、别名归一化或跨数据集类别映射 |
## 总体结论
当前前端真实可用的主链路是登录、Dashboard 后端概览、项目列表、新建项目、上传视频/DICOM、显式生成帧、浏览帧、播放帧、工作区手工绘制、点/框 AI 推理、视频片段传播、GT mask 导入、标注保存/回显、COCO 导出、PNG mask ZIP 导出、模板 CRUD。
当前前端真实可用的主链路是:JWT 登录、刷新恢复用户、退出登录、Dashboard 当前用户概览、当前用户项目列表、新建项目、上传视频/DICOM、显式生成帧、浏览帧、播放帧、工作区手工绘制、点/框 AI 推理、视频片段传播、GT mask 导入、标注保存/回显、COCO 导出、PNG mask ZIP 导出、模板 CRUD。
当前最主要的 Mock 或未打通链路是:真正的文本语义分割已因无文本提示入口而暂时禁用;复杂洞结构编辑、骨架/HDBSCAN 级别的 mask 降维增强、任务历史筛选、项目更多菜单和 mapping rules 运行时映射执行引擎仍未落地。登录页“安全审计说明文字”仍只是 UI 文案
当前最主要的 Mock 或未打通链路是:真正的文本语义分割已因无文本提示入口而暂时禁用;复杂洞结构编辑、骨架/HDBSCAN 级别的 mask 降维增强、任务历史筛选、项目更多菜单、全业务操作审计和 mapping rules 运行时映射执行引擎仍未落地。登录页“端到端加密”等安全文案仍只是 UI 文案;登录和用户管理操作审计已落库并可在管理员后台查看

View File

@@ -15,13 +15,14 @@ timeout: 30000
Authorization: Bearer <token>
```
当前后端多数接口没有鉴权依赖,所以这个 header 主要是前端侧行为
当前后端业务接口会校验该 header。缺失、过期或无效 token 返回 401项目、帧、标注、任务、Dashboard 和导出会按当前用户拥有的项目过滤
## 前端封装的 API
| 函数 | 方法与路径 | 状态 | 说明 |
|------|------------|------|------|
| `login(username, password)` | `POST /api/auth/login` | 对齐 | 后端返回 `{ token, username }`,前端只使用 token |
| `login(username, password)` | `POST /api/auth/login` | 对齐 | 后端返回 `{ token, token_type, username, user }`,前端保存 token 和当前用户 |
| `getCurrentUser()` | `GET /api/auth/me` | 对齐 | 用已有 Bearer token 恢复当前登录用户 |
| `getProjects()` | `GET /api/projects` | 对齐 | 前端映射 `frame_count``thumbnail_url` 等字段 |
| `createProject(payload)` | `POST /api/projects` | 对齐 | 支持 `name``description``parse_fps` |
| `updateProject(id, payload)` | `PATCH /api/projects/{id}` | 对齐 | 后端是 `PATCH /api/projects/{id}` |
@@ -46,10 +47,11 @@ Authorization: Bearer <token>
| `saveAnnotation(payload)` | `POST /api/ai/annotate` | 对齐 | 工作区归档保存当前项目未保存 mask |
| `updateAnnotation(annotationId, payload)` | `PATCH /api/ai/annotations/{annotation_id}` | 对齐 | 工作区归档保存 dirty mask |
| `deleteAnnotation(annotationId)` | `DELETE /api/ai/annotations/{annotation_id}` | 对齐 | 工作区清空当前帧已保存标注 |
| `importGtMask(file, projectId, frameId, templateId?)` | `POST /api/ai/import-gt-mask` | 对齐 | multipart 上传 GT mask,后端按非零像素值/连通域生成 polygon 标注和 seed point |
| `importGtMask(file, projectId, frameId, templateId?, options?)` | `POST /api/ai/import-gt-mask` | 对齐 | multipart 上传 GT mask;支持 `unknown_color_policy=discard/undefined`;后端仅接受灰度 maskid 图或 RGB 三通道完全相同的 `[X,X,X]` maskid 图0 为背景、X 为 maskid按模板 `maskId` 匹配类别,未知 maskid 可舍弃或导入为未定义类别;尺寸不同会最近邻拉伸到当前帧,连通域生成 polygon 标注和 seed point |
| `getDashboardOverview()` | `GET /api/dashboard/overview` | 对齐 | Dashboard 初始统计、队列和活动日志 |
| `exportCoco(projectId)` | `GET /api/export/{projectId}/coco` | 对齐 | 后端实际是 `GET /api/export/{project_id}/coco` |
| `exportMasks(projectId)` | `GET /api/export/{projectId}/masks` | 对齐 | 下载单标注 mask、语义融合 mask 和类别映射 ZIP |
| `exportSegmentationResults(projectId, options)` | `GET /api/export/{projectId}/results` | 对齐 | 新的统一导出入口;支持 `scope=all/range/current``outputs=separate,gt_label,pro_label,mix_label``mix_opacity``start_frame/end_frame``frame_id` 参数,返回包含 COCO JSON、maskid/GT 像素值映射、原始帧图片和所选 mask PNG 的 ZIP`mask_type=separate/gt_label/pro_label/mix_label/both` 仍兼容 |
## 后端 FastAPI 接口
@@ -58,6 +60,10 @@ Authorization: Bearer <token>
| 方法 | 路径 | 用途 |
|------|------|------|
| POST | `/api/auth/login` | 登录 |
| GET | `/api/auth/me` | 当前用户 |
| GET/POST/PATCH/DELETE | `/api/admin/users` | 管理员用户管理 |
| GET | `/api/admin/audit-logs` | 管理员审计日志 |
| POST | `/api/admin/demo-factory-reset` | 演示部署恢复出厂设置;请求体需 `confirmation=RESET_DEMO_FACTORY` |
| POST | `/api/projects` | 创建项目 |
| GET | `/api/projects` | 项目列表 |
| GET | `/api/projects/{project_id}` | 项目详情 |
@@ -92,6 +98,7 @@ Authorization: Bearer <token>
| GET | `/api/dashboard/overview` | Dashboard 聚合快照 |
| GET | `/api/export/{project_id}/coco` | 导出 COCO JSON |
| GET | `/api/export/{project_id}/masks` | 导出 PNG mask ZIP |
| GET | `/api/export/{project_id}/results` | 统一导出分割结果 ZIP包含 `annotations_coco.json``maskid_GT像素值_类别映射.json``原始图片/` 和按参数选择的 `分开Mask分割结果/``GT_label图/``Pro_label彩色分割结果/``Mix_label重叠覆盖彩色分割结果/`GT_label 背景为 0类别值使用模板中的真实 maskid缺失 maskid 的旧标注才补下一个可用正整数 |
| GET | `/health` | 健康检查 |
| WS | `/ws/progress` | WebSocket 进度通道,未出现在 OpenAPI paths 中 |
@@ -163,6 +170,7 @@ POST /api/media/parse?project_id=1&parse_fps=15&max_frames=120&target_width=960
"name": "胆囊",
"color": "#ffae00",
"zIndex": 280,
"maskId": 1,
"category": "腹腔镜胆囊切除术"
}
],
@@ -250,7 +258,7 @@ SAM 2 点提示和 auto fallback 当前只采用最高分候选 mask避免同
"bbox": [0.1, 0.1, 0.2, 0.2],
"label": "胆囊",
"color": "#ff0000",
"class_metadata": {"id": "c1", "name": "胆囊", "color": "#ff0000", "zIndex": 20},
"class_metadata": {"id": "c1", "name": "胆囊", "color": "#ff0000", "zIndex": 20, "maskId": 1},
"template_id": 2
}
}
@@ -293,7 +301,7 @@ SAM 2.1 变体使用对应 video predictor 的 mask seed 传播;`model=sam2`
- `getProjectAnnotations()` 已接入 `GET /api/ai/annotations`
- `updateAnnotation()` 已接入 `PATCH /api/ai/annotations/{annotationId}`
- `deleteAnnotation()` 已接入 `DELETE /api/ai/annotations/{annotationId}`
- `importGtMask()` 已接入 `POST /api/ai/import-gt-mask`,导入后端生成的 polygon 标注、原始 `gt_label_value` 和 seed point
- `importGtMask()` 已接入 `POST /api/ai/import-gt-mask`,导入后端生成的 polygon 标注、原始 `gt_label_value`、原图尺寸/是否拉伸信息和 seed point。导入端使用 `cv2.IMREAD_UNCHANGED` 保留低数值/16-bit GT_label 图的像素值;灰度图和 RGB 三通道相等图均按模板 `maskId` 匹配类别,不再按彩色图 RGB 颜色匹配类别;超出现有类别时由 `unknown_color_policy` 决定舍弃或写为 `gt_unknown_class` 未定义类别
- `exportMasks()` 已接入 `GET /api/export/{projectId}/masks`
- `parseMedia()` 已改为创建 Celery 后台任务,并返回 `ProcessingTask`
- `queuePropagationTask()` 已接入 `/api/ai/propagate/task`,自动传播不再依赖长时间同步 HTTP 请求。
@@ -302,8 +310,9 @@ SAM 2.1 变体使用对应 video predictor 的 mask seed 传播;`model=sam2`
- `retryTask()` 已接入 `POST /api/tasks/{taskId}/retry`
- `getDashboardOverview()` 已从 `processing_tasks` 聚合解析队列。
- Dashboard 任务列表已展示 queued/running/success/failed/cancelled 任务,并可通过 `getTask()` 查看失败详情;`summary.parsing_task_count` 仍只统计 queued/running。
- 工作区导出按钮已调用 `exportCoco()` / `exportMasks()`,并会先保存未归档 mask
- 工作区“分割结果导出”已调用 `exportSegmentationResults()`,并会先保存未归档 mask旧的 `exportCoco()` / `exportMasks()` 仍保留为兼容接口
- PNG mask ZIP 已包含每帧 `semantic_frame_*.png``semantic_classes.json`,重叠区域按 zIndex 裁决。
- 统一导出 ZIP 下载文件名为 `{项目库项目名}_seg_T_{起始时间戳}-{结束时间戳}_P_{起始项目帧序号}-{结束项目帧序号}.zip`;项目名来自 `Project.name` 并会替换文件系统不安全字符,时间戳来自帧 `timestamp_ms` 并格式化为 `0h00m00s000ms`,帧号使用项目抽帧后的 1-based `frame_index + 1`,不使用原视频 `source_frame_number`。ZIP 内包含 `annotations_coco.json``maskid_GT像素值_类别映射.json``原始图片/`。原始图片按 `视频名称_时间戳_项目帧序号` 命名;选择分开 mask 时写入 `分开Mask分割结果/{视频名称_时间戳_项目帧序号}_分别导出/{视频名称_时间戳_项目帧序号}_{类别名称}_maskid{maskid}.png`,同一帧同一类别会合并为一张二值 mask选择 GT_label 图时写入 `GT_label图/{视频名称_时间戳_项目帧序号}.png`;选择 Pro_label 彩色图时写入 `Pro_label彩色分割结果/{视频名称_时间戳_项目帧序号}.png`;选择 Mix_label 叠加图时写入 `Mix_label重叠覆盖彩色分割结果/{视频名称_时间戳_项目帧序号}.png`,透明度由 `mix_opacity` 控制,默认 0.3。导出时 maskid 与 GT_label 像素值相同;有模板 maskid 的类别保留真实 maskid缺失 maskid 的旧标注补下一个可用正整数并写入映射 JSON跨图一致maskid 不参与覆盖排序,覆盖顺序仍使用内部拖拽排序字段。
## 仍需处理的接口问题

View File

@@ -26,7 +26,7 @@
已完成:
1. 前端根据 `Mask.segmentation` 构造后端需要的 normalized `mask_data.polygons`
2. 用户点击“结构化归档保存”后,未保存 mask 调用 `POST /api/ai/annotate`dirty mask 调用 `PATCH /api/ai/annotations/{annotation_id}`
2. 用户点击顶栏保存状态按钮后,未保存 mask 调用 `POST /api/ai/annotate`dirty mask 调用 `PATCH /api/ai/annotations/{annotation_id}`;按钮文案会按待保存数量显示“保存 X 个改动”或“已全部保存”
3. 后端保存或更新 `project_id``frame_id``template_id``mask_data``bbox`;具体分类写入 `mask_data.class`
4. 工作区加载帧后调用 `GET /api/ai/annotations` 回显已保存标注。
5. 工作区“清空遮罩”调用 `DELETE /api/ai/annotations/{annotation_id}` 删除当前帧已保存标注。
@@ -37,15 +37,16 @@
2. 逐点几何编辑器已支持拖动/删除顶点、边中点插入新点和多 polygon 子区域编辑;后续增强为复杂洞结构编辑。
3. 区域合并/去除已支持基础 union/difference后续增强为更明确的多选列表、操作预览和冲突确认。
## 阶段 3接入导出按钮已完成 COCO JSON 和 PNG Mask ZIP
## 阶段 3接入导出按钮已完成统一分割结果导出
当前工作区“导出 JSON 标注集”和“导出 PNG Mask ZIP”都会先保存未归档 mask再调用后端导出接口
当前工作区“分割结果导出”会先保存未归档 mask再调用后端统一结果导出接口。旧 COCO JSON 和 PNG Mask ZIP 接口保留为兼容路径
已完成:
1. COCO JSON 调用 `/api/export/{projectId}/coco`
2. PNG Mask ZIP 调用 `/api/export/{projectId}/masks`
3. ZIP 保留单标注二值 `mask_*.png`,同时输出 `semantic_frame_*.png``semantic_classes.json`
3. 兼容 PNG Mask ZIP 保留单标注二值 `mask_*.png`,同时输出 `semantic_frame_*.png``semantic_classes.json`
4. 统一导出调用 `/api/export/{projectId}/results`,支持整体视频、特定范围帧、当前图片三种范围,以及分开 mask、GT_label 黑白图、Pro_label 彩色图和 Mix_label 原图叠加图ZIP 固定包含 maskid/GT 像素值映射 JSON 和原始图片文件夹,各输出文件夹按客户指定的 `视频名称_0h00m00s000ms_项目帧序号` 规则命名GT_label 图背景为 0类别值优先使用模板中的真实 maskid缺失 maskid 的旧标注才补下一个可用正整数。
剩余建议:
@@ -98,7 +99,7 @@ Word 方案中的完整版本包含距离变换、骨架提取和聚类。当前
已完成:
1. 工作区提供“导入 GT Mask”入口。
1. 工作区左侧工具栏提供“导入 GT Mask”入口,位置在“重叠区域去除”之后
2. 前端调用 `POST /api/ai/import-gt-mask` multipart 接口。
3. 后端按非零像素值拆分多类别 mask。
4. 后端使用 OpenCV contour 提取每个类别下的连通域。
@@ -117,10 +118,10 @@ Word 方案中的完整版本包含距离变换、骨架提取和聚类。当前
已完成:
1. 标注保存时记录 template class id / name / zIndex
2. 导出 mask 时按 zIndex 从低到高覆盖。
1. 标注保存时记录 template class id / name / maskid并保留内部覆盖优先级
2. 导出 mask 时按内部优先级从低到高覆盖。
3. 同类语义值在融合图中共享同一个 class value。
4. 跨类重叠由高 zIndex 覆盖低 zIndex
4. 跨类重叠由更高内部优先级覆盖更低内部优先级maskid 不作为排序规范
剩余建议:

View File

@@ -7,15 +7,21 @@
## R1 登录与会话
- 系统提供登录页。
- 默认开发凭证为 `admin / 123456`
- 登录成功后前端保存 token,并进入主应用。
- 默认开发管理员为启动时种子化的 `admin / 123456`,密码以哈希形式存入 `users`
- 登录成功后前端保存签名 JWT,并进入主应用。
- 页面刷新后前端会用已有 token 调用 `/api/auth/me` 恢复当前用户。
- 登录失败时显示错误信息。
- 当前 token 是开发用固定 token不做真实 JWT 校验
- 业务接口必须校验 Bearer token缺失或无效 token 返回 401
- 项目、帧、标注、任务、Dashboard 和导出必须按当前用户的项目隔离;用户不能读取、修改或删除其他用户项目资源。
- 角色包括 `admin``annotator``viewer``admin/annotator` 可写入业务数据和触发 AI/传播,`viewer` 只能访问读接口,用户管理后台仅 `admin` 可用。
- 管理员侧栏显示“用户管理”入口;管理员可以新增用户、修改角色、停用/启用、修改密码、删除无项目用户。
- 系统记录登录成功/失败和用户管理操作到 `audit_logs`,管理员后台可查看最近审计日志。
- 管理员后台提供“恢复演示出厂设置”危险操作;前端必须二次确认,后端也必须校验 `confirmation=RESET_DEMO_FACTORY`,执行后只保留默认 admin 账号、系统模板和一个尚未生成帧的演示视频项目,清空其它用户、项目、帧、标注、任务、用户模板和旧审计记录,并写入本次重置审计。
## R2 项目管理
- 前端展示项目库,并从 `GET /api/projects` 获取项目列表。
- 用户可以新建项目,前端调用 `POST /api/projects`
- 用户可以新建项目,前端调用 `POST /api/projects`;后端把项目归属到当前登录用户
- 用户可以选择项目,进入工作区。
- 用户可以导入视频文件,前端创建项目、上传文件并刷新项目列表;导入视频不自动拆帧。
- 用户可以对已导入且尚未生成帧的视频项目点击“生成帧”,在弹窗中选择目标 FPS 后创建拆帧任务。
@@ -48,28 +54,34 @@
- 若项目有媒体但无帧,工作区只提示需要先在项目库生成帧,不再自动触发拆帧。
- Canvas 显示当前帧图片。
- Canvas 支持滚轮缩放、移动工具拖拽、鼠标坐标显示。
- Canvas 未选中特定 mask 时mask 显示顺序必须遵循右侧“语义分类树”拖拽得到的内部覆盖优先级:低优先级先渲染,高优先级后渲染并显示在上层;选中 mask 后可以为了编辑交互临时置顶。
- 时间轴支持缩略图点击切帧、range 拖动切帧、视频处理进度条点击切帧、人工/AI 标注帧和自动传播帧标识点击切帧、键盘左右方向键切帧、播放/暂停顺序推进帧。
- 清空片段遮罩进入范围选择后必须提供两种模式:`清空全部` 会清空范围内所有 mask若包含人工绘制或 AI 智能分割生成的“人工/AI 标注帧”必须弹出“是否清除“人工/AI标注帧””确认`保留人工/AI` 只清空范围内自动传播/推理 mask人工/AI 标注帧必须保留且不弹出人工帧确认;用户取消确认时不能删除本地 mask、后端标注或传播历史条。
- 用户在某帧选中 mask 后,如果切换到同一自动传播结果覆盖的其他帧,工作区应自动识别并选中目标帧中对应的传播 mask匹配依据为传播结果回显到 mask metadata 的 seed 来源和传播链字段,而不是仅凭标签或颜色。
- 播放帧率使用项目 `parse_fps``original_fps`,限制在 1 到 30 FPS。
- 时间轴显示当前帧时间和总时长,时间基准使用项目 `parse_fps``original_fps`,格式为 `mm:ss.cc`
- 时间轴顶部播放进度条只表达当前播放位置;其下方的视频处理进度条表达处理状态:人工绘制或 AI 智能分割生成的帧显示红色竖线,自动传播生成的帧显示蓝色区段,最近自动传播处理过的片段叠加不同色系的横向渐变条,片段内部随时间从深到浅,帮助识别最近处理范围;未处理背景使用中性灰以和标记保持明显区分。底部帧可视化栏中,人工/AI 标注帧缩略图边框为红色,自动传播/推理帧缩略图边框为蓝色,当前帧仍用青色外框高亮优先;如果同一帧既有人工/AI 标注又有自动传播结果,红色人工/AI 标注框优先保留,自动传播状态只作为蓝色内描边或次级提示;如果当前帧同时是人工/AI 标注帧,则显示青色外框加红色内描边,外层选中框和内层标注框顺序不能交换。
- 时间轴顶部播放进度条只表达当前播放位置;其下方的视频处理进度条表达处理状态:当前帧位置用白色竖线贯穿播放进度条和视频处理进度条;人工绘制或 AI 智能分割生成的帧显示红色竖线,自动传播生成的帧显示蓝色区段,最近自动传播处理过的片段叠加同一蓝色系纯色条,按距最新传播的时间顺序逐次变暗,且第 5 次及更早统一为阈值旧记录色,帮助识别第一次、第二次、第 N 次传播清空片段遮罩后与清空范围重叠的最近传播历史条必须同步移除或裁剪不应继续显示已经清空的传播范围未处理背景使用中性灰以和标记保持明显区分。进入自动传播或清空遮罩范围选择时起始帧和结束帧必须额外显示两条贯穿两条进度条的高对比边界线颜色避开青色播放进度、红色标注、蓝色传播、amber 选区和深色背景。底部帧可视化栏中,人工/AI 标注帧缩略图边框为红色,自动传播/推理帧缩略图边框为蓝色,当前帧仍用青色外框高亮优先;如果同一帧既有人工/AI 标注又有自动传播结果,红色人工/AI 标注框优先保留,自动传播状态只作为蓝色内描边或次级提示;如果当前帧同时是人工/AI 标注帧,则显示青色外框加红色内描边,外层选中框和内层标注框顺序不能交换。
- 自动传播提交前支持独立选择传播权重,范围限定为 SAM 2.1 tiny/small/base+/large 四个权重变体;该选择只影响传播任务,不提供 SAM2/SAM3 家族切换,也不改变 AI 智能分割页的单帧推理权重。
## R5 工具栏
- 工具栏可以切换当前 active tool。
- 正向点、反向点、框选工具会影响 Canvas 交互
- 工作区左侧工具栏不展示正向点、反向点、框选工具;这些入口只属于 AI 智能分割页
- 侧栏“AI智能分割”和工作区工具栏 AI 跳转入口必须使用带明确 AI 语义的图标,而不是普通魔法棒等泛化工具图标。
- 魔法棒按钮切换到 AI 页面。
- 多边形、矩形、圆、点、线工具会在 Canvas 上生成可保存的 polygon mask。
- 多边形通过点击取点并按 Enter 完成,也支持三点后点击首节点闭合;矩形、圆、线通过拖拽生成;点工具生成小点区域
- 多边形、矩形、圆、画笔、橡皮擦工具会在 Canvas 上生成或编辑可保存的 polygon mask;左侧工具栏不再提供创建点和创建线段入口
- 多边形通过点击取点并按 Enter 完成,也支持三点后点击首节点闭合;矩形、圆通过拖拽生成;画笔和橡皮擦支持调整大小
- 画笔工具只在语义分类树有选中类别时可用,按住拖动时以圆形笔触采样,鼠标松开后一次性 union 成连续区域;如果笔触与当前选中 mask 连通,默认合并到该 mask否则生成新的当前类别 mask。
- 橡皮擦工具只在当前帧已选中 mask 时可用,按住拖动时以圆形笔触采样,鼠标松开后从选中 mask 中 difference 扣除;扣空时删除该 mask已保存 mask 仍需同步后端删除。
- 创建多边形、创建矩形、区域合并/去除、调整多边形等 Canvas 左上角上下文提示只作为短提示,切换工具或操作状态变化时显示,数秒后自动隐藏,避免长期遮挡待编辑图像;再次切换工具或操作状态变化会重新显示。
- 绘制工具点击已有 mask 时应继续执行当前绘制动作,不应被 mask 选择逻辑吞掉。
- 工具栏提供“调整多边形”工具,用户可以点击 mask 进入 polygon 顶点编辑态;按住顶点即可直接拖动并实时更新 mask 几何,不需要先单击选中顶点,已保存 mask 会标记为 dirty顶点和 seed point 等子节点拖拽不能冒泡成画布拖拽,编辑结束后 Canvas 当前缩放和平移视口必须保持不变。
- 工具栏在“重叠区域去除”之后提供“导入 GT Mask”入口该入口使用区别于普通编辑工具的紫色底色不切换 activeTool。
- 顶点编辑态显示边中点插入手柄;点击边中点会在该边中间新增顶点。
- “调整多边形”工具下双击 polygon 边界时,会在最接近的线段上按双击位置新增顶点。
- 顶点编辑态下选中顶点后可用 Delete/Backspace 删除顶点,但不会让 polygon 少于三点。
- 选中整个 mask 且未选中具体顶点时Delete/Backspace 删除该 mask已保存 mask 同步调用后端删除接口。
- 撤销、重做绑定全局 `maskHistory/maskFuture`支持工具栏按钮、AI 页按钮和 Canvas 快捷键
- 撤销、重做绑定全局 `maskHistory/maskFuture`工作区支持顶栏按钮和 Canvas 快捷键AI 页支持自己的按钮;左侧工具栏不重复放置撤销/重做入口
- 区域合并工具支持多选当前帧 mask并使用 polygon union 生成合并后的主 mask。
- 区域去除工具支持多选当前帧 mask并从第一个选中的主 mask 中扣除后续选中 mask。
- 区域合并/去除模式显示已选数量,并隐藏 polygon 编辑手柄以避免手柄抢占多选点击;第一个选中的主区域使用黄色实线轮廓,后续参与合并/扣除的区域使用红色虚线轮廓。
@@ -93,11 +105,13 @@
- SAM 2.1 不支持文本语义提示;当前 AI 页面不提供文本语义输入,必须使用点/框提示。
- SAM 2.1 点提示和 auto fallback 默认只采用一个最高分候选 mask避免多个候选 mask 作为同一结果重叠显示。
- AI 页面只渲染本页最新生成的候选 mask重复执行高精度分割会替换上一次 AI 页候选,工作区已有手工、保存、传播或 GT 导入 mask 不会自动进入 AI 画布,也不会被替换。
- AI 页面提供“遮罩清晰度”滑杆,调节本页候选 mask 预览透明度,不改变 mask 几何、分类或保存数据。
- AI 页面提供“AI 遮罩透明度”滑杆,并与右侧“遮罩透明度”共享 `maskPreviewOpacity`;调节任一入口都会改变 AI 候选 mask 预览透明度,不改变 mask 几何、分类或保存数据。
- AI 页面参数开关展示文案使用“局部专注模式(自动裁剪无锚区域)”和“严格除杂模式(自动清理干涉点)”;这是 UI 可读性文案,不改变 `cropMode``autoDeleteBg` 或后端 `options` 字段。
- AI 页面生成的 SAM 2.1 mask 会写入全局 `masks`,自动同步到当前项目帧,并写入全局 `selectedMaskIds`;右侧语义分类树可以直接给新生成 mask 换标签。
- AI 页“清空全体锚点”只清空本页提示点和本页生成的候选 mask不删除工作区已有 mask。
- AI 页面“推送至工作区编辑”会切回工作区并把工具切到“调整多边形”,保留当前选中的 AI mask 和当前帧视角,以便继续编辑轮廓和归档保存;如果 AI 操作发生在非第一帧,回到工作区后不得强制跳回第一帧
- AI 页面“推送至工作区编辑”必须先校验待推送 AI 候选 mask 已有语义分类;没有 `classId``className` 时用右上角 error toast 明确提示并停留在 AI 页,不允许进入工作区,确保工作区内 mask 都有语义
- 如果用户不通过推送按钮而是直接离开 AI 页面,仍未选择语义分类的 AI 候选 mask 必须从全局 `masks``selectedMaskIds` 中清理,避免无语义候选通过侧栏切换进入工作区。
- AI 页面“推送至工作区编辑”在语义校验通过后会切回工作区并把工具切到“调整多边形”,保留当前选中的 AI mask 和当前帧视角,以便继续编辑轮廓和归档保存;如果 AI 操作发生在非第一帧,回到工作区后不得强制跳回第一帧。
- 工作区加载后端已保存标注时,必须保留当前项目帧里尚未保存的 AI/手工 draft mask避免 AI 页推送到工作区的候选 mask 被异步回显流程覆盖。
- 语义文本提示 `semantic` 当前被后端禁用并返回 400。
- SAM 3 源码和历史测试保留,但不属于当前产品可用功能;前端不再展示 SAM 3 入口,后端 registry 不暴露 `sam3`
@@ -108,13 +122,13 @@
- 前端会把多个 seed 或双向范围拆成 `steps`,通过 `POST /api/ai/propagate/task` 创建 `propagate_masks` 后台任务,避免长 HTTP 请求卡在浏览器侧,同时避免并发抢占 GPU。
- `POST /api/ai/propagate` 作为单 seed 同步兼容接口保留;`POST /api/ai/propagate/task` 是工作区自动传播使用的任务接口。两者当前支持四个 SAM 2.1 变体;兼容 `model=sam2` 并归一化为 tiny。SAM 2.1 使用官方 `SAM2VideoPredictor.add_new_mask()``propagate_in_video()`
- 自动传播任务写入 `processing_tasks`,前端轮询 `GET /api/tasks/{task_id}` 显示进度并刷新标注Dashboard 也能看到该任务,任务可取消和重试。
- 传播结果会写入后续帧 `annotations``mask_data.source` 标记为 `<model_id>_propagation`,并保留 label、color、class 元数据、seed 来源 id、seed 签名传播方向 `geometry_smoothing` 边缘平滑参数。
- 自动传播任务必须避免重复叠加:同一目标帧段内,同一参考 seed、同一权重、同一方向、同一平滑参数且所有目标帧已有未变化结果时worker 直接跳过;同一参考 seed 已变化、目标帧段只部分覆盖用户改用其他 SAM 2.1 权重或修改平滑参数worker 先删除本次目标帧段内对应旧自动传播标注,再保存新传播结果;对早期只记录前端临时 `source_mask_id` 的旧传播结果worker 会按传播方向和语义信息做兼容清理。用户在自动传播链中间帧人工新增或修改同一物体 mask 后重新向前/向后传播时,即使新 seed 缺少旧传播链 source id也要按语义信息和目标帧空间重叠清理旧传播结果后再写入新结果写入前清理不受旧结果 `propagation_direction` 限制,因此当前帧向前传播时也会替换原先由更早帧向后传播出来的旧 mask避免同一物体新旧 mask 堆叠。未编辑的自动传播结果再次作为参考 seed 时,会继承原始 `propagation_seed_signature` 以避免重复传播;被编辑后的传播结果只保留 lineage不继承旧签名以便触发删除旧结果并重新传播。带 `geometry_smoothing` 的 seed 在 forward/backward 两个方向都会用同一参数平滑保存结果。
- 传播结果会写入后续帧 `annotations``mask_data.source` 标记为 `<model_id>_propagation`,并保留 label、color、class 元数据、seed 来源 id、seed 签名传播方向;如果历史或外部 seed 带 `geometry_smoothing` 平滑参数worker 保存前仍必须对传播返回的 polygon 实际应用同一平滑几何,不能只更新拓扑锚点或 metadata。当前工作区平滑按钮应用后会直接改写实际 polygon 并清除平滑参数,后续传播以新几何本身参与签名
- 自动传播任务必须避免重复叠加:同一目标帧段内,同一参考 seed、同一权重、同一方向且所有目标帧已有未变化结果时worker 直接跳过;同一参考 seed 已变化、目标帧段只部分覆盖用户改用其他 SAM 2.1 权重时worker 先删除本次目标帧段内对应旧自动传播标注,再保存新传播结果;对早期只记录前端临时 `source_mask_id` 的旧传播结果worker 会按传播方向和语义信息做兼容清理。用户在自动传播链中间帧人工新增或修改同一物体 mask 后重新向前/向后传播时,即使新 seed 缺少旧传播链 source id也要按语义信息和目标帧空间重叠清理旧传播结果后再写入新结果写入前清理不受旧结果 `propagation_direction` 限制,因此当前帧向前传播时也会替换原先由更早帧向后传播出来的旧 mask避免同一物体新旧 mask 堆叠。未编辑的自动传播结果再次作为参考 seed 时,会继承原始 `propagation_seed_signature` 以避免重复传播;被编辑后的传播结果只保留 lineage不继承旧签名以便触发删除旧结果并重新传播。历史`geometry_smoothing` 的 seed 在 forward/backward 两个方向都会用同一参数平滑保存结果。
- AI 页面会对未放置点提示、后端错误和返回 0 个 mask 的情况显示明确反馈。
- AI 参数支持 `crop_to_prompt``auto_filter_background``min_score`;点/框 prompt 可以裁剪局部区域推理并回映射结果,背景过滤会移除低分结果和包含负向点的 polygon。
- 后端返回 `polygons``scores`
- 前端把后端 `polygons` 转成 Konva `pathData``segmentation``bbox``area`
- AI 推理结果先存放在前端 store 的 `masks` 中,点击“结构化归档保存后持久化到后端标注表。
- AI 推理结果先存放在前端 store 的 `masks` 中,顶栏保存状态按钮会按待保存数量显示“保存 X 个改动”或“已全部保存”;点击保存后持久化到后端标注表。
## R7 标注保存
@@ -123,12 +137,15 @@
- 后端提供 `GET /api/ai/annotations` 查询项目标注,可选按 `frame_id` 过滤。
- 后端提供 `PATCH /api/ai/annotations/{annotation_id}` 更新已保存标注的 `mask_data``points``bbox``template_id`
- 后端提供 `DELETE /api/ai/annotations/{annotation_id}` 删除已保存标注。
- 当前前端“结构化归档保存”会保存当前项目未保存 mask并会更新已标记为 dirty 的已保存 mask。
- 当前前端保存状态按钮会保存当前项目未保存 mask并会更新已标记为 dirty 的已保存 mask。
- 保存成功后,前端会重新拉取后端标注,并用后端 saved annotation 替换本次提交的 draft mask未提交的其他 draft mask 仍保留。
- 工作区“清空遮罩”会删除当前帧已保存标注,并清空当前帧未保存 mask。
- 工作区加载项目帧后会查询已保存标注并回显。
- 工作区支持导入 GT mask 图片,前端调用 `POST /api/ai/import-gt-mask`
- 后端导入 GT mask 时按非零像素值拆分多类别区域,再按连通域生成 polygon 标注,并通过距离变换写入 seed point
- 导入 GT Mask 时,前端必须让用户选择未知 maskid 处理策略:舍弃未知类别,或导入为“未定义类别”等待后续重新命名
- 后端导入 GT mask 时必须支持二值 mask、灰度/16-bit `GT_label图`,以及 RGB 三通道完全相同的 `[X,X,X]` maskid 图0 是背景X 是 maskid。灰度/RGB 等通道图按当前模板 `maskId` 匹配类别,超出现有类别时按用户选择的策略处理;普通彩色 RGB 类别图不再视为合法 GT mask必须返回图片不符合要求的明确错误。
- 导入 GT mask 前端必须提供导入结果预览,显示检测到的 maskid、未知 maskid 和尺寸适配提示;如果 mask 图片尺寸与当前帧不同,后端导入前必须按当前帧长宽用最近邻插值拉伸,使 mask 可适配当前图片。
- 后端导入 GT mask 时按非背景像素值或颜色拆分多类别区域,再按连通域生成 polygon 标注,并通过距离变换写入 seed point。
- 前端会回显导入标注的 seed point拖动 seed point 后,已保存标注会变为 dirty归档保存时会更新后端 `points`
## R8 模板库
@@ -136,7 +153,7 @@
- 前端展示模板列表,调用 `GET /api/templates`
- 用户可以新建、编辑、删除模板。
- 模板分类存放在 `mapping_rules.classes`,规则存放在 `mapping_rules.rules`
- 前端支持添加/删除分类、拖拽排序后重算 `zIndex`、JSON 批量导入、加载腹腔镜默认分类。
- 前端支持添加/删除分类、拖拽排序后更新内部覆盖优先级、JSON 批量导入、加载腹腔镜默认分类。界面不展示内部优先级数值,只展示每个类别稳定的 `maskid`
- 后端支持模板创建、列表、详情、局部更新和删除。
## R9 本体检查面板
@@ -144,11 +161,12 @@
- 工作区右侧可以选择模板。
- 面板显示模板分类;新增自定义分类会写入当前激活模板的后端 `mapping_rules.classes`
- 用户可以选择具体分类;新 AI mask 会记录 `classId``className``classZIndex`,并在保存时写入 `mask_data.class`
- 如果 Canvas 当前已经选中一个或多个 mask点击语义分类树会把这些 mask 的 `label``color` 和 class 元数据改为该分类;已保存 mask 会进入 `dirty` 状态,归档保存时更新后端。
- 如果 Canvas 当前已经选中一个或多个 mask点击语义分类树会把这些 mask 的 `label``color` 和 class 元数据改为该分类;如果这些 mask 属于自动传播链,还必须通过 `source_annotation_id``source_mask_id``propagation_seed_key` 同步更新同一传播链前后帧的对应 mask已保存 mask 会进入 `dirty` 状态,归档保存时更新后端。
- 添加自定义分类需要先选择模板,保存时调用 `PATCH /api/templates/{id}` 并同步全局模板 store。
- “特定目标实例属性追踪”下方显示当前选中 mask 的 `className/label`,不显示全局 active class 的旧值。
- 选中 mask 后,拓扑锚点和重新提取拓扑锚点按钮调用 `POST /api/ai/analyze-mask`,不再显示固定占位值;前端不再展示“后端模型置信度”条目
- 选中 mask 后,右侧实例属性面板提供“边缘平滑强度”和“应用边缘平滑”;应用时调用 `POST /api/ai/smooth-mask`,后端返回平滑后的 polygon、bbox、area 和拓扑锚点,前端将 mask 标记为 dirty/draft用户仍需通过结构化归档保存落库
- 当前实例属性面板不展示“当前选中区域”计数;当前 mask 交互以单选为主,计数长期为 1不作为有效业务信息展示
- 选中 mask 后,拓扑锚点调用 `POST /api/ai/analyze-mask` 自动读取,不再显示固定占位值;后端 `topology_anchor_count` 必须表示 polygon 的真实顶点数量,不能用抽样后的展示点数代替;前端必须静默忽略 abort/cancel 或过期的分析请求,避免快速切换 mask、拖动平滑预览或卸载组件时误显示“后端属性读取失败”前端不再展示“后端模型置信度”条目也不再提供“重新提取拓扑锚点”调试按钮
- 选中 mask 后,右侧实例属性面板提供“边缘平滑强度”和“应用边缘平滑”;调整滑杆时必须立即更新数值,但后端预览请求需要做短防抖,用户停止拖动约 220ms 后再调用 `POST /api/ai/smooth-mask` 并用返回 polygon 临时预览当前 mask 边缘,避免连续拖动时请求过密造成卡顿;预览阶段不标 dirty点击“应用边缘平滑”后确认当前预览结果前端必须把平滑 polygon 作为新的实际 mask 几何写入当前 mask并同步写入同一传播链前后对应 mask整次平滑应用必须作为一个撤销/重做历史步骤,撤销/重做要同时作用于当前 mask 和传播链对应 mask应用后相关 mask 标记为 dirty/draft平滑强度重置为 0用户仍可继续用 polygon 编辑工具编辑平滑后的新多边形,并通过顶栏保存状态按钮落库。后端平滑必须对 AI/SAM 密集轮廓执行去噪简化、Chaikin 平滑和二次简化,使结果 polygon 的密集边缘点实际减少;强度映射必须低段温和、高段继续递进,避免 20% 左右已经过度平滑且后续档位无明显变化。
## R10 Dashboard 与 WebSocket
@@ -168,11 +186,21 @@
- 后端支持 `GET /api/export/{project_id}/coco` 导出 COCO JSON。
- 后端支持 `GET /api/export/{project_id}/masks` 导出 PNG mask ZIP。
- 后端支持 `GET /api/export/{project_id}/results` 统一导出分割结果 ZIP参数支持整体视频、特定范围帧和当前图片三种范围并支持分开二值 mask、GT_label 黑白图、Pro_label 彩色图和 Mix_label 原图叠加图Mix_label 透明度默认 0.3。
- 统一导出 ZIP 必须固定包含 `maskid_GT像素值_类别映射.json`,记录当前导出中每个类别的 `maskid`、GT_label 像素值、中文名、类别名、RGB 值、颜色和类别 keyGT_label 背景值固定为 0语义类别值使用类别真实 maskid缺失 maskid 的旧标注才补下一个可用正整数,且同一类别跨图片保持一致。
- 统一导出 ZIP 必须固定包含 `原始图片/` 文件夹,导出范围内每帧的原始图片命名为 `视频名称_时间戳_项目帧序号` 加原图片扩展名;视频名称来自项目视频文件名,时间戳来自帧 `timestamp_ms` 并格式化为 `0h00m00s000ms`,帧号使用项目抽帧后的 1-based `frame_index + 1`,不使用原视频帧号。
- 选择“分开 Mask”时统一导出 ZIP 必须包含 `分开Mask分割结果/`;每帧建立 `{视频名称_时间戳_项目帧序号}_分别导出` 子文件夹,同一帧同一类别的所有 annotation 合并为一张二值 PNG文件名包含 `视频名称_时间戳_项目帧序号_{类别名称}_maskid{maskid}`
- 选择“GT_label 黑白图”时,统一导出 ZIP 必须包含 `GT_label图/`;每帧输出一张融合后的 GT_label PNG文件名为 `视频名称_时间戳_项目帧序号`重叠区域仍按内部拖拽排序从低到高覆盖maskid 不构成排序规范。
- 选择“Pro_label 彩色图”时,统一导出 ZIP 必须包含 `Pro_label彩色分割结果/`;每帧输出一张按类别 RGB 上色的 PNG背景为 `[0,0,0]`
- 选择“Mix_label 叠加图”时,统一导出 ZIP 必须包含 `Mix_label重叠覆盖彩色分割结果/`;每帧输出一张彩色 label 叠加原始图片的 PNG透明度可选且默认为 0.3。
- GT_label、Pro_label 和 Mix_label 的重叠区域覆盖顺序必须和右侧“语义分类树”的内部覆盖优先级一致,低优先级先写入,高优先级后写入。
- 分割结果导出 ZIP 文件名必须使用 `{项目库项目名}_seg_T_{起始时间戳}-{结束时间戳}_P_{起始项目帧序号}-{结束项目帧序号}.zip`;项目名来自项目库中的 `Project.name`,时间戳来自导出范围首尾帧 `timestamp_ms` 并格式化为 `0h00m00s000ms`,帧号使用项目抽帧后的 1-based `frame_index + 1`
- 当前前端 `exportCoco()` API 封装已对齐后端路径。
- 当前前端 `exportMasks()` API 封装已对齐后端路径。
- 工作区“导出 JSON 标注集”按钮已绑定下载事件;导出前会先保存当前未归档 mask
- 工作区“导出 PNG Mask ZIP”按钮已绑定下载事件导出前会先保存当前未归档 mask
- 当前前端 `exportSegmentationResults()` API 封装已对齐统一导出路径
- 工作区“分割结果导出”按钮已替代原 JSON/PNG 两个按钮;点击后在下拉栏内选择导出范围、勾选导出内容,并在选择 Mix_label 时调节遮罩透明度和查看当前/待导出第一帧预览;导出范围默认选中“当前图片”,导出前会先保存当前未归档 mask。选择“特定范围帧”时用户既可以直接修改起止帧输入框也可以像自动传播、清空遮罩一样在播放进度条或视频处理进度条上点击/拖拽选择导出范围
- PNG mask ZIP 包含单标注二值 mask、按 zIndex 融合后的每帧语义 mask 和 `semantic_classes.json`
- 统一导出的 GT_label 图背景值固定为 0所有语义类别值优先保留模板类别真实 maskid缺失 maskid 的旧标注才按下一个可用正整数补值。
## R12 配置

View File

@@ -29,9 +29,9 @@
| 项目库 | `src/components/ProjectLibrary.tsx` | 项目列表、新建、删除、导入视频/DICOM、显式生成帧 |
| 工作区 | `src/components/VideoWorkspace.tsx` | 加载帧和模板组织工具栏、Canvas、本体面板、时间轴 |
| Canvas | `src/components/CanvasArea.tsx` | 显示帧、缩放平移、点/框提示、渲染 mask |
| 工具栏 | `src/components/ToolsPalette.tsx` | 切换工具、跳转 AI 页面、触发 mask 撤销/重做;紧凑垂直布局,高度不足时自身滚动;外层宽 56px按钮列固定 48px滚动条使用右侧外扩空间和低对比 `seg-scrollbar` |
| 工作区顶栏 | `src/components/VideoWorkspace.tsx` | 保存/导出/传播/按起止帧批量清空遮罩/导入 GT、显式撤销/重做按钮和工作区快捷键 |
| 时间轴 | `src/components/FrameTimeline.tsx` | 帧导航、播放进度、视频处理进度条、自动传播历史片段、自动传播范围选择、左右方向键切帧、播放和当前/总时长显示 |
| 工具栏 | `src/components/ToolsPalette.tsx` | 切换工作区编辑工具、在“重叠区域去除”后触发 GT Mask 导入、跳转 AI 页面AI 跳转入口复用 Bot + Sparkles 组合图标以明确表达 AI 智能分割;不再放置 AI 正/反点和框选工具,也不重复放置撤销/重做;拖拽/选择到创建圆、画笔/橡皮擦/区域合并/重叠区域去除、导入 GT Mask/AI 智能分割三类工具之间用浅灰横线分隔;紧凑垂直布局,高度不足时自身滚动;外层宽 56px按钮列固定 48px滚动条使用右侧外扩空间和低对比 `seg-scrollbar` |
| 工作区顶栏 | `src/components/VideoWorkspace.tsx` | 保存状态按钮(“保存 X 个改动”/“已全部保存”)、导出/传播/按起止帧批量清空遮罩、显式撤销/重做按钮和工作区快捷键 |
| 时间轴 | `src/components/FrameTimeline.tsx` | 帧导航、播放进度、视频处理进度条、自动传播历史片段、自动传播/清空遮罩/导出范围选择、左右方向键切帧、播放和当前/总时长显示 |
| 本体面板 | `src/components/OntologyInspector.tsx` | 模板选择、工作区 mask 透明度、分类树、后端自定义分类、mask 后端属性分析;内容过长时自身滚动,滚动条使用低对比 `seg-scrollbar` |
| AI 页面 | `src/components/AISegmentation.tsx` | 独立 AI 推理视图,使用当前项目帧 |
| 模板库 | `src/components/TemplateRegistry.tsx` | 模板 CRUD、分类编辑、导入、排序 |
@@ -44,9 +44,10 @@
| 应用入口 | `backend/main.py` | FastAPI app、CORS、路由注册、健康检查、WebSocket |
| 配置 | `backend/config.py` | Pydantic settings |
| 数据库 | `backend/database.py` | SQLAlchemy engine、session、Base |
| 模型 | `backend/models.py` | Project、Frame、Template、Annotation、Mask、ProcessingTask |
| 模型 | `backend/models.py` | User、Project、Frame、Template、Annotation、Mask、AuditLog、ProcessingTask |
| Schema | `backend/schemas.py` | Pydantic 请求/响应模型 |
| Auth | `backend/routers/auth.py` | 开发登录 |
| Auth | `backend/routers/auth.py` | 用户表、密码哈希、JWT 登录和 `/api/auth/me` |
| Admin | `backend/routers/admin.py` | 管理员用户 CRUD、角色/密码/启停用和审计日志 |
| Projects | `backend/routers/projects.py` | 项目与帧 CRUD |
| Templates | `backend/routers/templates.py` | 模板 CRUD 和 mapping_rules 打包/解包 |
| Media | `backend/routers/media.py` | 上传媒体和拆帧 |
@@ -77,7 +78,19 @@
1. `Login` 收集用户名和密码。
2. `login()` 调用 `POST /api/auth/login`
3. 成功后 store 写入 tokenApp 渲染主界面
3. 后端用 `users` 表中的密码哈希校验用户,成功后返回签名 JWT 和用户资料
4. 前端把 token 写入 `localStorage` 和 Zustand刷新页面时 `useStore` 会从 `localStorage` 恢复 token。
5. `App` 在已登录状态调用 `/api/auth/me` 恢复当前用户,再拉取当前用户项目列表。
### 用户隔离
1. `Project.owner_user_id` 指向 `users.id`;启动时默认 admin 用户会被创建,历史 `owner_user_id IS NULL` 的项目会迁移归属到 admin。
2. 项目、帧、媒体上传/拆帧、AI 标注、传播任务、任务列表、Dashboard 和导出接口都通过当前 JWT 用户过滤项目资源。
3. `Template.owner_user_id` 支持用户模板;`owner_user_id IS NULL` 的模板视为系统模板,可作为默认分类体系对用户可见。
4. 角色分为 `admin``annotator``viewer``admin/annotator` 可调用写入类业务接口,`viewer` 只能调用读接口;`/api/admin/*` 仅允许 `admin`
5. `UserAdmin.tsx` 仅在当前用户角色为 `admin` 时从 `Sidebar` 展示,调用 `/api/admin/users` 完成新增、角色修改、停用/启用、密码修改和删除无项目用户,调用 `/api/admin/audit-logs` 展示登录和管理操作审计;危险区“恢复演示出厂设置”先用浏览器确认,再要求输入 `RESET_DEMO_FACTORY`,随后调用 `/api/admin/demo-factory-reset`
6. `POST /api/admin/demo-factory-reset` 仅允许 `admin`,会重置默认 admin 密码/角色/启用状态删除其它用户、项目、帧、标注、mask、任务、用户模板和旧审计重新创建 `Data_MyVideo_1` 项目并上传 `settings.demo_video_path` 指向的视频作为未生成帧的源视频项目;系统模板保留以保证重置后仍可标注。
7. 缺失、过期或伪造的 Bearer token 会在业务路由返回 401权限不足返回 403其他用户项目资源对当前用户表现为 404。
### 项目导入与生成帧
@@ -109,14 +122,15 @@
5. `VideoWorkspace` 加载项目帧时会优先按当前选中 mask 的 `frameId` 和当前打开帧 id 恢复 `currentFrameIndex`;只有没有可恢复帧时才回到第一帧,避免 AI 页在非第一帧推送回工作区时视角被重置。
6. `CanvasArea` 会把全局 `selectedMaskIds` 中仍存在于当前帧的 id 同步回本地选区,避免帧初始化时的临时清空覆盖 AI 页推送过来的选中态;如果切换到另一帧时原 id 不存在,但目标帧存在同一自动传播链的结果,前端会用 `source_annotation_id``source_mask_id``propagation_seed_key` 匹配对应传播 mask 并自动选中。
7. `CanvasArea` 根据容器和帧尺寸按 86% 适配比例计算初始 scale/position使底图默认居中且尽量大但保留画布边距滚轮缩放和拖拽平移仍由用户后续控制。
8. `FrameTimeline` 顶部播放进度条显示当前播放位置;其下方视频处理进度条根据 `Mask.metadata.source` / `propagated_from_frame_id` 计算自动传播帧并显示蓝色区段,对人工绘制或 AI 智能分割等非传播 mask 帧显示红色竖线。普通状态下,视频处理进度条可点击跳转到对应帧,红色人工/AI 标注帧和蓝色自动传播帧标识本身也可点击跳转。处理条未处理背景使用中性灰,和红色/蓝色标记保持明显区分。`VideoWorkspace` 会记录当前会话最近 8 次成功处理过的自动传播范围,并通过 `propagationHistory` 传给 `FrameTimeline`;时间轴会把这些片段叠加为不同色系的横向渐变条,片段内按视频时间从深到浅,较早片段降低透明度。底部缩略图导航轴对非当前帧使用红色边框标识人工/AI 标注帧,使用蓝色边框标识自动传播/推理帧;如果同一帧同时存在人工/AI 标注和自动传播结果,红色人工/AI 标注边框优先保留,自动传播状态只作为蓝色内描边。当前帧使用青色外框高亮优先,若当前帧同时是人工/AI 标注帧,则以青色外框加红色内描边同时表达两个状态,外层当前帧框和内层人工/AI 框的顺序固定。工作区进入自动传播或清空片段遮罩范围选择模式时,播放进度条和视频处理进度条显示 amber 覆盖层,并可点击/拖拽设置处理起止帧
9. 当前帧传入 `CanvasArea`
10. 工作区顶栏短状态文本会在空闲状态下自动消失;保存、导出、导入 GT 和传播任务运行中仍保留进度状态,无帧项目提示也会保留
11. 左侧工具栏和右侧本体/语义分类面板使用 `seg-scrollbar` 定制纵向滚动条;默认滚动条 thumb 低透明度融入深色背景hover/focus 时增强为青色提示,避免系统默认滚动条在工具区中过于突兀。左侧工具栏额外保留右侧滚动条槽位,按钮列仍按原 48px 布局,避免滚动条和图标抢空间
8. `CanvasArea` 未选中特定 mask 时,会按 `classZIndex` 从低到高渲染当前帧 mask该值来自右侧“语义分类树”的拖拽排序因此高优先级类别会后渲染并覆盖低优先级类别。有选中 mask 时,编辑态可保留选中区域置顶,方便拖点、换类和布尔操作
9. `FrameTimeline` 顶部播放进度条显示当前播放位置;其下方视频处理进度条根据 `Mask.metadata.source` / `propagated_from_frame_id` 计算自动传播帧并显示蓝色区段,对人工绘制或 AI 智能分割等非传播 mask 帧显示红色竖线。当前帧另用白色竖线贯穿播放进度条和视频处理进度条,和青色播放进度、红色标注、蓝色传播状态区分。普通状态下,视频处理进度条可点击跳转到对应帧,红色人工/AI 标注帧和蓝色自动传播帧标识本身也可点击跳转。处理条未处理背景使用中性灰,和红色/蓝色标记保持明显区分。`VideoWorkspace` 会记录当前会话最近 8 次成功处理过的自动传播范围,并通过 `propagationHistory` 传给 `FrameTimeline`;时间轴会把这些片段叠加为同一蓝色系的纯色条,按距最新传播的时间顺序逐次变暗,且第 5 次及更早统一为阈值旧记录色,不再在单个片段内部使用渐变。清空片段遮罩时,`VideoWorkspace` 会按清空范围移除或裁剪本地传播历史片段,避免已清空的处理范围仍显示最近传播条。底部缩略图导航轴对非当前帧使用红色边框标识人工/AI 标注帧,使用蓝色边框标识自动传播/推理帧;如果同一帧同时存在人工/AI 标注和自动传播结果,红色人工/AI 标注边框优先保留,自动传播状态只作为蓝色内描边。当前帧使用青色外框高亮优先,若当前帧同时是人工/AI 标注帧,则以青色外框加红色内描边同时表达两个状态,外层当前帧框和内层人工/AI 框的顺序固定。工作区进入自动传播、清空片段遮罩或特定范围帧导出选择模式时,播放进度条和视频处理进度条显示 amber 覆盖层,并额外用洋红色起始线和黄绿色结束线贯穿两条进度条,表达待处理或待导出范围边界,可点击/拖拽设置起止帧
10. 当前帧传入 `CanvasArea`
11. 工作区顶栏短状态文本会在空闲状态下自动消失;保存、导出、导入 GT 和传播任务运行中仍保留进度状态,无帧项目提示也会保留
12. 左侧工具栏和右侧本体/语义分类面板使用 `seg-scrollbar` 定制纵向滚动条;默认滚动条 thumb 低透明度融入深色背景hover/focus 时增强为青色提示,避免系统默认滚动条在工具区中过于突兀。左侧工具栏额外保留右侧滚动条槽位,按钮列仍按原 48px 布局,避免滚动条和图标抢空间。
12. 右侧面板不再显示“本体论与属性分类管理树”固定说明栏,直接展示实际可操作内容。
13. 右侧“遮罩透明度”滑杆写入 Zustand `maskPreviewOpacity``CanvasArea` 用该值计算 mask group opacity选中 mask 在基础透明度上加亮,方便保留选中反馈。
13. 右侧“遮罩透明度”滑杆写入 Zustand `maskPreviewOpacity``CanvasArea` `AISegmentation`用该值计算 mask group opacity选中 mask 在基础透明度上加亮或按基础透明度显示,方便保留选中反馈。
14. Canvas 点击 mask 后,全局 `selectedMaskIds` 会同步到 `OntologyInspector`;本体面板按选中 mask 的 `classId``className/label` 和颜色匹配模板分类,自动设置 active class并把分类按钮滚动/聚焦到可见区域。
15. 工作区顶栏“清空片段遮罩”和“自动传播”共用时间轴范围选择交互;第一次点击“清空片段遮罩”会进入范围选择模式,按钮变为“确认清空”,用户可在播放进度条或视频处理进度条上点击/拖拽选择起止帧;确认执行时对范围内已保存 mask 调用 `DELETE /api/ai/annotations/{id}`,同时移除范围内本地 draft mask被清空的选区范围外 mask 保持不变。
15. 工作区顶栏“清空片段遮罩”和“自动传播”共用时间轴范围选择交互;第一次点击“清空片段遮罩”会进入范围选择模式,按钮变为“确认清空”,用户可在播放进度条或视频处理进度条上点击/拖拽选择起止帧;进入清空模式后顶栏显示“清空全部 / 保留人工/AI”两段式模式选择默认“清空全部”。“清空全部”会对范围内已保存 mask 调用 `DELETE /api/ai/annotations/{id}`,同时移除范围内本地 draft mask被清空的选区和与清空范围重叠的本地传播历史条;若范围内存在非自动传播来源的 mask也就是时间轴红色“人工/AI 标注帧”,执行前会弹出“是否清除“人工/AI标注帧””确认取消则不删除任何 mask。“保留人工/AI”只删除范围内自动传播/推理 mask不删除人工绘制或 AI 智能分割生成的红色标注帧,不弹出人工帧确认;范围外 mask 和传播历史片段保持不变。
### AI 点/框推理
@@ -136,12 +150,12 @@
14. AI 页面提示点由本地 `points` 状态维护;点击已渲染提示点会按 index 删除对应点,“删除最近锚点”会删除数组最后一个点,不改动候选 mask 列表。
15. AI 页面候选 mask 删除只接受当前 `aiMaskIds` 范围内的已选 id“删除选中候选”和 Delete/Backspace 都复用该范围过滤,避免删除工作区已有 mask。
16. AI 页面参数开关文案只做展示增强:“局部专注模式(自动裁剪无锚区域)”仍控制 `cropMode/crop_to_prompt`,“严格除杂模式(自动清理干涉点)”仍控制 `autoDeleteBg/auto_filter_background/min_score`
17. AI 页面“遮罩清晰度”滑杆只调节候选 mask 的 Konva preview opacity不写入 `Mask.segmentation`、分类元数据或后端 payload。
17. AI 页面“AI 遮罩透明度”滑杆复用 Zustand `maskPreviewOpacity`,和右侧“遮罩透明度”联动,只调节候选 mask 的 Konva preview opacity不写入 `Mask.segmentation`、分类元数据或后端 payload。
18. AI 画布左上角根据正向点、反向点、边界框选和视口控制显示上下文提示,说明点击/拖拽、删除提示点和执行推理的操作方式。
19. AI 画布根据容器和当前帧尺寸按 86% 适配比例计算初始 scale/position使底图默认居中且尽量大但保留画布边距。
20. Canvas 按当前帧过滤并渲染 mask。
21. 新 mask 会带上当前选择的模板分类元数据,包括 `classId``className``classZIndex``metadata.source=ai_segmentation` 和保存状态 `draft`
20. 用户点击“结构化归档保存后,前端将像素 `segmentation` 转成 normalized `mask_data.polygons`;未保存 mask 调用 `POST /api/ai/annotate`dirty mask 调用 `PATCH /api/ai/annotations/{annotation_id}`;保存成功后本次提交的 draft mask id 会从本地保留列表中排除,并由后端 saved annotation 回显替换。
20. 顶栏保存状态按钮按当前项目待保存数量显示为“保存 X 个改动”或“已全部保存”;用户点击保存后,前端将像素 `segmentation` 转成 normalized `mask_data.polygons`;未保存 mask 调用 `POST /api/ai/annotate`dirty mask 调用 `PATCH /api/ai/annotations/{annotation_id}`;保存成功后本次提交的 draft mask id 会从本地保留列表中排除,并由后端 saved annotation 回显替换。
21. 工作区加载项目帧后通过 `GET /api/ai/annotations` 取回已保存标注并转成前端 mask。
22. 工作区“清空遮罩”删除当前帧已保存标注,并清除当前帧本地 mask。
@@ -151,26 +165,27 @@
2. 用户可以直接修改传播起始帧/结束帧数字框,并可通过工作区顶栏“传播权重”下拉独立选择本次传播使用的 SAM 2.1 tiny/small/base+/large 权重;该入口不提供 SAM2/SAM3 家族切换,默认跟随全局 AI 权重,用户手动选择后不再被 AI 页权重切换覆盖。
3. `VideoWorkspace` 以当前参考帧为 seed将起止帧拆成 `backward` 和/或 `forward` 两段;只包含当前帧时不传播。
4. `VideoWorkspace` 在提交传播前会先调用现有归档保存链路保存当前项目中的 draft/dirty mask并重新读取 store 中的回显结果;参考帧 seed 因此优先携带稳定的后端 `source_annotation_id`,避免用前端临时 mask id 生成传播结果后,二次传播无法找到旧结果。
5. `VideoWorkspace``buildAnnotationPayload()` 把每个 seed mask 转成 normalized polygon、bbox、label、color、class 元数据、`geometry_smoothing``source_mask_id` 和可用时的 `source_annotation_id`;如果 seed mask 是未编辑的自动传播结果,会沿用其原始 `source_annotation_id/source_mask_id/propagation_seed_signature`,让后端把它识别为原传播链的同一个 seed如果该传播结果被编辑并保存更新 payload 只保留 lineage不保留旧签名使后端按“已修改”路径清理旧结果并重传。
5. `VideoWorkspace``buildAnnotationPayload()` 把每个 seed mask 转成 normalized polygon、bbox、label、color、class 元数据、`source_mask_id` 和可用时的 `source_annotation_id`;如果 seed mask 是未编辑的自动传播结果,会沿用其原始 `source_annotation_id/source_mask_id/propagation_seed_signature`,让后端把它识别为原传播链的同一个 seed如果该传播结果被编辑并保存更新 payload 只保留 lineage不保留旧签名使后端按“已修改”路径清理旧结果并重传。对历史或外部写入的 `geometry_smoothing` metadatapayload 仍可透传给后端兼容处理;当前前端平滑应用会直接改写 polygon 几何并移除该参数。
6. 前端把传播权重 id、每个 seed、每个方向组装成 `steps`,一次调用 `POST /api/ai/propagate/task``include_source=false``save_annotations=true`;接口先规范化/校验 `model` 字段中的权重 id再创建 `processing_tasks.task_type=propagate_masks` 并投递 Celery避免长 HTTP 请求阻塞前端等待。
7. `VideoWorkspace` 记录返回的 `task_id`,轮询 `GET /api/tasks/{task_id}` 显示任务 message、步骤进度、已处理帧次和已保存区域数任务运行期间提供取消传播按钮调用通用 `POST /api/tasks/{task_id}/cancel`
8. Celery worker 逐 step 顺序执行传播,避免多个视频 tracker 并发抢占 GPU每个 step 开始/完成都会写入 `processing_tasks.progress/result/message` 并发布 Redis `seg:progress`Dashboard 可同步显示。每个 step 开始前worker 会在本次目标帧段内用 seed 来源 id、传播方向和包含 `geometry_smoothing` seed 签名查找旧传播标注:同权重、签名相同且目标帧都已有结果时跳过该 seed签名不同、目标帧只部分覆盖本次使用了其他 SAM 2.1 权重或平滑参数变化则先删除本次目标帧段内对应方向的旧自动传播标注,再执行新的 video predictor 传播。对旧版本只记录前端临时 `source_mask_id` 的传播标注worker 会按 label/color/class 做兼容匹配,确保可被后续稳定 `source_annotation_id` 的传播替换;对中间帧人工新增的替代 seed若缺少旧 source idworker 仍会用语义信息识别候选旧传播结果,并在写入目标帧新 polygon 前用目标帧 bbox 重叠做二次确认和清理。写入前这层清理不限制旧结果方向,确保 backward 传播可覆盖早先 forward 传播留下的同物体旧 mask。
8. Celery worker 逐 step 顺序执行传播,避免多个视频 tracker 并发抢占 GPU每个 step 开始/完成都会写入 `processing_tasks.progress/result/message` 并发布 Redis `seg:progress`Dashboard 可同步显示。每个 step 开始前worker 会在本次目标帧段内用 seed 来源 id、传播方向和 seed 签名查找旧传播标注:同权重、签名相同且目标帧都已有结果时跳过该 seed签名不同、目标帧只部分覆盖本次使用了其他 SAM 2.1 权重则先删除本次目标帧段内对应方向的旧自动传播标注,再执行新的 video predictor 传播;若历史 seed 签名中包含 `geometry_smoothing`,仍按完整签名参与兼容去重。对旧版本只记录前端临时 `source_mask_id` 的传播标注worker 会按 label/color/class 做兼容匹配,确保可被后续稳定 `source_annotation_id` 的传播替换;对中间帧人工新增的替代 seed若缺少旧 source idworker 仍会用语义信息识别候选旧传播结果,并在写入目标帧新 polygon 前用目标帧 bbox 重叠做二次确认和清理。写入前这层清理不限制旧结果方向,确保 backward 传播可覆盖早先 forward 传播留下的同物体旧 mask。
9. 后端按项目帧序列截取片段,下载对应帧到临时目录,并写成 `000000.jpg` 这类纯数字文件名;这是 `SAM2VideoPredictor` 对视频帧排序的要求,和项目库中持久化的 `frame_%06d.jpg` 对象名无关。
10. `model` 为任一 SAM 2.1 权重变体时,`sam2_engine` 使用对应 checkpoint/config 加载 `SAM2VideoPredictor.add_new_mask()` 注入 seed mask再用 `propagate_in_video()` 传播;`model=sam2` 会在入队时规范化为 tiny任务 payload/result 会保留规范化后的权重 id单个 SAM2 video predictor 调用内部暂不提供逐帧流式进度。
11. `model=sam3` 当前不支持SAM 3 video tracker 代码保留但没有接入产品路径。
12. 后端把传播返回的 normalized polygon 保存为后续帧 `Annotation`,跳过源帧;如果 seed 带 `geometry_smoothing`,保存前会用同一 Chaikin 平滑参数处理 forward/backward 两个方向的结果。`mask_data.source` 记录权重传播来源,同时写入 `propagation_seed_key``propagation_seed_signature``propagation_direction``source_annotation_id``source_mask_id` `geometry_smoothing` 供后续幂等传播判断。
12. 后端把传播返回的 normalized polygon 保存为后续帧 `Annotation`,跳过源帧;如果历史或外部 seed 带 `geometry_smoothing`,保存前会用同一平滑参数处理 forward/backward 两个方向的结果:强度先经过缓入曲线映射,低强度使用较小 Chaikin 切角比例和简化阈值,高强度再逐步增加迭代、切角和简化力度;随后按强度对 SAM 密集轮廓做 `approxPolyDP` 去噪简化,再做 Chaikin 平滑,最后二次简化并以平滑后的 polygon 计算 bbox 后落库。当前工作区“应用边缘平滑”会在前端把同传播链对应 mask 直接改写为新的 polygon 并移除 `geometry_smoothing` 参数,因此后续传播通常按新几何本身参与 seed 签名`mask_data.source` 记录权重传播来源,同时写入 `propagation_seed_key``propagation_seed_signature``propagation_direction``source_annotation_id``source_mask_id` 供后续幂等传播判断;历史 `geometry_smoothing` 仅在存在时保留用于兼容判断。
13. 前端轮询到已创建区域后刷新 `GET /api/ai/annotations` 并回显新标注;任务结束后如果后端返回 0 个新区域,工作区会明确提示没有生成新的 mask若是未改变 seed 被跳过则提示未改变 mask 已跳过。处理过帧次大于 0 的成功任务会追加一条本地传播历史片段,用于视频处理进度条显示最近传播范围;`annotationToMask()` 会保留传播来源 metadata供时间轴视频处理进度条显示蓝色传播区段。
### 手工绘制与历史栈
1. 用户在 `ToolsPalette` 选择多边形、矩形、圆、点或线工具
1. 用户在 `ToolsPalette` 选择多边形、矩形、圆、画笔或橡皮擦工具;创建点和创建线段入口不在工作区左侧工具栏中提供
2. `CanvasArea` 将交互坐标转换成像素 polygon。
3. 多边形工具逐次记录节点,三点后点击首节点或按 Enter 时生成闭合 polygon。
4. Canvas 左上角根据当前工具和操作阶段显示上下文短提示;多边形提示会随已放置点数切换,明确 Enter 完成、Esc 取消和点击首节点闭合。提示会在工具或操作状态变化时出现,并在数秒后自动隐藏,避免长期遮挡底图。
5. mask path 只在 `move``edit_polygon``area_merge``area_remove` 工具下拦截点击;绘制和 AI prompt 工具点击已有 mask 时继续冒泡给 Stage。
6. 新 mask 写入 `pathData`、像素 `segmentation``bbox``area` 和当前模板分类元数据
7. `addMask()``setMasks()``updateMask()``clearMasks()` 会维护 `maskHistory/maskFuture`
8. 工具栏按钮、工作区顶栏按钮和 AI 页按钮调用 `undoMasks()` / `redoMasks()`;工作区由 `VideoWorkspace` 统一处理 `Ctrl/Cmd+Z``Ctrl/Cmd+Shift+Z``Ctrl/Cmd+Y`,并在输入框、下拉框和可编辑文本聚焦时跳过快捷键,避免影响帧范围输入
5. mask path 只在 `move``edit_polygon``area_merge``area_remove` 工具下拦截点击;绘制、画笔、橡皮擦和 AI prompt 工具点击已有 mask 时继续冒泡给 Stage。
6. 画笔/橡皮擦尺寸保存在 Zustand 中;拖动期间只保留采样后的圆形笔触预览,鼠标松开后再用 `polygon-clipping` 执行 union/difference避免拖动中反复重算复杂 polygon
7. 新 mask 写入 `pathData`、像素 `segmentation``bbox``area` 和当前模板分类元数据
8. `addMask()``setMasks()``updateMask()``clearMasks()` 会维护 `maskHistory/maskFuture`
9. 工作区撤销/重做只保留顶栏按钮和快捷键入口AI 页保留自己的撤销/重做按钮;工作区由 `VideoWorkspace` 统一处理 `Ctrl/Cmd+Z``Ctrl/Cmd+Shift+Z``Ctrl/Cmd+Y`,并在输入框、下拉框和可编辑文本聚焦时跳过快捷键,避免影响帧范围输入。
### Polygon 逐点编辑
@@ -196,7 +211,7 @@
### GT Mask 导入
1. 工作区“导入 GT Mask”选择图片文件。
1. 工作区左侧工具栏“导入 GT Mask”选择图片文件;入口位于“重叠区域去除”之后
2. 前端 `importGtMask()` 以 multipart form-data 调用 `POST /api/ai/import-gt-mask`,携带 `project_id``frame_id`
3. 后端验证项目、帧、模板后使用 OpenCV 读取灰度 mask。
4. 后端按非零像素值拆分多类别标签。
@@ -214,13 +229,16 @@
5. 返回时再解包给前端。
6. `CanvasArea` 把当前选中的 mask id 同步到全局 `selectedMaskIds`;切换工具、切换帧或卸载 Canvas 时会清空选择。
7. `AISegmentation` 生成 mask 后会写入全局 `masks` 并把生成的 mask id 写入 `selectedMaskIds`;点击 AI 页预览 mask 也会更新 `selectedMaskIds`
8. AI 页“推送至工作区编辑”会切换到工作区并把 `activeTool` 设为 `edit_polygon``CanvasArea` 初始读取全局 `selectedMaskIds`,让 AI 页选中的 mask 在工作区继续保持选中
9. 工作区帧/标注异步加载完成后,`hydrateSavedAnnotations()` 会合并本地未保存 draft mask 和后端已保存 mask不会用后端回显结果直接覆盖整个 `masks` store
10. `OntologyInspector` 可以选择具体分类;选择结果进入全局 store`CanvasArea``AISegmentation` 新建/更新 mask 时使用
11. 如果 `selectedMaskIds` 中存在当前 store 的 mask点击分类时会立即更新这些 mask 的 `templateId``classId``className``classZIndex``label``color`
12. 同一次点击会把这些已选 mask 移动到前端 `masks` 数组末尾;`CanvasArea` 按数组顺序渲染,后渲染的 Path 显示在最上层,方便用户继续编辑刚换标签的区域。该显示置顶不改变模板 `zIndex` 或后端导出语义覆盖规则
13. 已保存 mask 被重新分类后进入 `dirty``saved=false`,继续复用工作区归档保存的 PATCH 链路
14. 模板保存、删除和 JSON 导入失败使用 `TransientNotice` 非阻塞提示,默认数秒后自动消失
8. AI 页“推送至工作区编辑”会先检查待推送 AI 候选 mask 是否具备 `classId` `className`;缺少语义分类时清空普通推理反馈,并通过 `TransientNotice` 右上角 error toast 提示用户先点右侧语义分类树,不切换模块、不修改工具状态
9. `AISegmentation` 卸载时会清理仍缺少 `classId/className` 的本页 AI 候选,并同步移除对应 `selectedMaskIds`,避免用户绕过推送按钮从侧栏切到工作区时带入无语义 mask
10. AI 页语义校验通过后会切换到工作区并把 `activeTool` 设为 `edit_polygon``CanvasArea` 初始读取全局 `selectedMaskIds`,让 AI 页选中的 mask 在工作区继续保持选中
11. 工作区帧/标注异步加载完成后,`hydrateSavedAnnotations()` 会合并本地未保存 draft mask 和后端已保存 mask不会用后端回显结果直接覆盖整个 `masks` store
12. `OntologyInspector` 可以选择具体分类;选择结果进入全局 store`CanvasArea``AISegmentation` 新建/更新 mask 时使用
13. 如果 `selectedMaskIds` 中存在当前 store 的 mask点击分类时会立即更新这些 mask 的 `templateId``classId``className``classZIndex``label``color`
14. 对属于自动传播链的 mask分类更新会复用 `source_annotation_id``source_mask_id``propagation_seed_key` 查找同一目标实例在前后帧中的传播结果,并同步更新这些传播 mask 的分类元数据,避免同一物体跨帧语义不一致
15. 同一次点击会把这些已选 mask 移动到前端 `masks` 数组末尾;`CanvasArea` 按数组顺序渲染,后渲染的 Path 显示在最上层,方便用户继续编辑刚换标签的区域。该显示置顶不改变模板 `zIndex` 或后端导出语义覆盖规则。
16. 已保存 mask 被重新分类后进入 `dirty``saved=false`,同传播链被同步更新的已保存 mask 也进入 `dirty`,继续复用工作区归档保存的 PATCH 链路。
16. 模板保存、删除和 JSON 导入失败使用 `TransientNotice` 非阻塞提示,默认数秒后自动消失。
### 导出
@@ -228,7 +246,9 @@
2. PNG mask 导出会把 normalized polygon 渲染为单标注二值 mask。
3. PNG mask 导出还会按 `mask_data.class.zIndex` 或模板 `z_index` 从低到高覆盖,生成每帧语义融合 mask。
4. ZIP 内写入 `semantic_classes.json`,记录语义值到类别、颜色和 zIndex 的映射。
5. 前端“导出 JSON 标注集”和“导出 PNG Mask ZIP”按钮都会在导出前保存待归档标注然后下载对应文件
5. 前端使用“分割结果导出”统一入口替代原 JSON/PNG 两个按钮;点击后在下拉栏选择整体视频、特定范围帧或当前图片,默认选中当前图片,并勾选分开二值 mask、GT_label 黑白图、Pro_label 彩色图和 Mix_label 原图叠加图。选择“特定范围帧”时,导出起止帧输入框和 `FrameTimeline` 的范围拖拽选择共用同一组导出范围状态;选择 Mix_label 时显示透明度滑杆,默认 0.3,并用当前/待导出第一帧做遮罩预览。提交前会保存待归档标注,然后下载统一 ZIP。下载文件名使用 `{项目库项目名}_seg_T_{起始时间戳}-{结束时间戳}_P_{起始项目帧序号}-{结束项目帧序号}.zip`;项目名来自 `currentProject.name`,起止帧按当前导出范围取首尾帧,时间戳格式为 `0h00m00s000ms`,帧号使用项目抽帧后的 1-based 顺序,项目名中的文件系统不安全字符会替换为 `_`
6. 统一导出 ZIP 固定包含 `annotations_coco.json``maskid_GT像素值_类别映射.json``原始图片/`;原始图片文件名使用 `视频名称_时间戳_项目帧序号`。导出会保留类别真实 maskidGT_label 像素值与 maskid 相同并跨图一致;缺失 maskid 的旧标注才补下一个可用正整数并写入映射 JSON。选择分开 mask 时包含 `分开Mask分割结果/`,每帧建立 `{视频名称_时间戳_项目帧序号}_分别导出` 子文件夹,并按“同一帧同一类别合并一张图”的方式输出 `{视频名称_时间戳_项目帧序号}_{类别名称}_maskid{maskid}.png`。选择 GT_label 图时包含 `GT_label图/{视频名称_时间戳_项目帧序号}.png`;选择 Pro_label 图时包含 `Pro_label彩色分割结果/{视频名称_时间戳_项目帧序号}.png`;选择 Mix_label 图时包含 `Mix_label重叠覆盖彩色分割结果/{视频名称_时间戳_项目帧序号}.png`。GT_label、Pro_label 和 Mix_label 的重叠区域按内部拖拽排序从低到高覆盖和未选中状态下的画布显示顺序一致maskid 不参与排序。后端直接下载接口的 `Content-Disposition` 使用同一 ZIP 命名规则,并用 `filename*` 支持中文项目名。
7. 右侧 `OntologyInspector` 的语义分类树支持拖拽调整内部覆盖顺序;拖拽后保存到模板并同步当前工作区同类 mask 的 `classZIndex`,但保留类别 maskid 不变。
## 接口契约
@@ -237,13 +257,14 @@
- `updateProject()` 使用 `PATCH /api/projects/{id}`
- `exportCoco()` 使用 `GET /api/export/{projectId}/coco`
- `exportMasks()` 使用 `GET /api/export/{projectId}/masks`
- `exportSegmentationResults()` 使用 `GET /api/export/{projectId}/results`,通过 query 参数选择范围和 mask 类型。
- `cancelTask()` 使用 `POST /api/tasks/{taskId}/cancel`
- `retryTask()` 使用 `POST /api/tasks/{taskId}/retry`
- `predictMask()` 使用 `POST /api/ai/predict`,请求体为 `image_id``prompt_type``prompt_data``model`
- `propagateMasks()` 使用 `POST /api/ai/propagate`,请求体为 `project_id``frame_id``model``seed``direction``max_frames`,作为单 seed 同步兼容接口保留。
- `queuePropagationTask()` 使用 `POST /api/ai/propagate/task`,请求体为 `project_id``frame_id``model``steps``include_source``save_annotations`,返回 `ProcessingTask`
- `saveAnnotation()` 使用 `POST /api/ai/annotate`
- `importGtMask()` 使用 `POST /api/ai/import-gt-mask` multipart form-data。
- `importGtMask()` 使用 `POST /api/ai/import-gt-mask` multipart form-data,并传入 `unknown_color_policy=discard|undefined`。前端上传前弹出导入结果预览和未知 maskid 策略选择;后端使用 `cv2.IMREAD_UNCHANGED` 保留低数值/16-bit GT_label 像素值。合法 GT mask 限定为灰度图或 RGB 三通道完全相同的 `[X,X,X]` maskid 图0 为背景、X 为 maskid灰度/RGB 等通道图按模板 `maskId` 匹配类别,普通彩色 RGB 类别图不再按颜色匹配并会返回格式错误;未知类别按策略舍弃或保存为 `gt_unknown_class` 未定义类别。若 GT mask 尺寸和当前帧不同,后端用最近邻插值拉伸到当前帧尺寸后再生成 polygon
- `getProjectAnnotations()` 使用 `GET /api/ai/annotations`
- `updateAnnotation()` 使用 `PATCH /api/ai/annotations/{annotationId}`
- `deleteAnnotation()` 使用 `DELETE /api/ai/annotations/{annotationId}`
@@ -276,5 +297,5 @@
- 已保存标注支持通过“应用分类”、polygon 顶点拖动/删除、边中点插入、多 polygon 子区域编辑和区域合并/去除进入 dirty 状态并归档更新;选中整块 mask 可用 Delete/Backspace 删除并同步后端;复杂洞结构编辑尚未实现。
- SAM 3 文本语义分割已从当前产品路径中禁用相关源码保留恢复时需要重新接入前端入口、registry、状态接口和测试。
- 自定义分类通过 `PATCH /api/templates/{id}` 写入当前激活模板的 `mapping_rules.classes`
- 选中 mask 后,本体面板的“特定目标实例属性追踪”标题值来自当前 mask 的 `className/label`,不使用全局 active class面板调用 `POST /api/ai/analyze-mask` 显示拓扑锚点数量等属性,“重新提取拓扑锚点”会带 `extract_skeleton=true` 重新请求后端分析;“边缘平滑强度/应用边缘平滑”调用 `POST /api/ai/smooth-mask`,由后端按 Chaikin smoothing 返回新 polygon 并把 `geometry_smoothing` 写回 mask metadata。前端不再展示“后端模型置信度”。
- 选中 mask 后,本体面板的“特定目标实例属性追踪”标题值来自当前 mask 的 `className/label`,不使用全局 active class面板不再展示长期为 1 的“当前选中区域”计数;面板调用 `POST /api/ai/analyze-mask` 自动显示拓扑锚点数量等属性,`topology_anchor_count` 是真实 polygon 顶点数量,`topology_anchors` 只保留最多 64 个抽样点用于调试展示;`OntologyInspector` 会为分析请求维护递增序号,旧请求返回时不再回写状态,并静默忽略 Axios abort/cancel 错误,避免快速切换、平滑预览或组件卸载时把正常中止误报成失败;不再提供“重新提取拓扑锚点”调试按钮;“边缘平滑强度”滑杆会即时更新数值,但 `POST /api/ai/smooth-mask` 预览请求经过约 220ms 防抖后才发送,返回 polygon 作为临时预览写入当前 mask 显示,预览不改变保存状态;点击“应用边缘平滑”后,前端把平滑 polygon 作为新的实际几何写入当前 mask并按传播 lineage 同步写入传播链前后对应 mask相关 mask 标记为 dirty/draft整次操作通过一次 `setMasks()` 进入撤销/重做历史;应用后不保留 `geometry_smoothing` 参数,平滑强度重置为 0。前端不再展示“后端模型置信度”。
- GT mask 导入已完成多类别像素值拆分、contour、distance transform seed point 和前端 seed point 拖拽编辑骨架提取、HDBSCAN 聚类和模板自动映射尚未实现。

View File

@@ -14,17 +14,17 @@
| 需求 | 测试文件 | 覆盖点 |
|------|----------|--------|
| R1 登录与会话 | `src/components/Login.test.tsx`, `backend/tests/test_auth.py` | 成功登录、失败提示、后端 401 |
| R2 项目管理 | `src/lib/api.test.ts`, `src/components/ProjectLibrary.test.tsx`, `backend/tests/test_projects.py` | 前端字段映射、PATCH 更新、项目卡片删除、DELETE 契约、后端 CRUD、删除级联、帧列表 |
| R1 登录与会话 | `src/components/Login.test.tsx`, `src/components/Sidebar.test.tsx`, `src/components/UserAdmin.test.tsx`, `src/store/useStore.test.ts`, `backend/tests/test_auth.py`, `backend/tests/test_admin.py` | 成功登录、JWT/token 写入、当前用户写入、刷新恢复基础状态、失败提示、登录输入 autocomplete、后端 401、`/api/auth/me`、管理员入口、用户 CRUD、角色权限、审计日志、viewer 读写权限边界、演示出厂设置二次确认和重置结果 |
| R2 项目管理 | `src/lib/api.test.ts`, `src/components/ProjectLibrary.test.tsx`, `backend/tests/test_projects.py` | 前端字段映射、PATCH 更新、项目卡片删除、DELETE 契约、后端 CRUD、删除级联、帧列表、项目按当前 JWT 用户隔离 |
| R3 媒体上传与拆帧 | `src/components/ProjectLibrary.test.tsx`, `src/components/TransientNotice.test.tsx`, `backend/tests/test_media.py`, `backend/tests/test_tasks.py` | 视频导入不自动拆帧、显式生成帧 FPS 选择、项目卡片显示目标 parse_fps 而非原视频 FPS、扩展名校验、自动建项目、关联项目、创建异步任务、非阻塞自动消失操作提示、标准帧序列参数、帧时间戳/源帧号、任务序列元数据、worker 注册帧、取消任务、重试任务、取消后 worker 停止 |
| R4 工作区与帧浏览 | `src/components/VideoWorkspace.test.tsx`, `src/components/FrameTimeline.test.tsx` | 加载帧、无帧项目不自动解析并提示生成帧、工作区短状态自动消失、工作区/AI 画布底图默认居中且保留边距、工作区 mask 透明度、回显已保存标注时保留本地未保存 draft mask、选中 mask 后跨帧自动跟随同一传播链结果、清空片段遮罩进入时间轴范围选择并按选区批量清空、传播权重下拉深色可读配色、缩略图/range/视频处理进度条、视频处理进度条点击跳帧、人工/AI 标注帧红色竖线和标识点击跳帧、自动传播帧蓝色区段和标识点击跳帧、最近自动传播历史片段不同色系渐变显示、缩略图红/蓝边框、人工/AI 标注帧叠加传播状态时红框优先保留并显示蓝色内描边、当前人工/AI 标注帧青色外框加红色内描边、普通状态不显示传播范围黄色选区、播放进度条和视频处理进度条选择传播/清空范围、当前帧由播放进度条末端和缩略图青色高亮表达/左右方向键切帧、播放、按项目 FPS 显示当前/总时长 |
| R5 工具栏 | `src/components/ToolsPalette.test.tsx`, `src/components/CanvasArea.test.tsx`, `src/components/VideoWorkspace.test.tsx`, `src/store/useStore.test.ts` | 工具切换、工具栏紧凑垂直布局和高度不足时滚动、工具栏低对比滚动条、工具栏外扩滚动条槽位不挤占按钮列、调整多边形工具、AI 跳转、矩形/圆/线/点/多边形手工 mask 绘制、点工具在已有 mask 上落点、多边形 Enter/首节点闭合、上下文提示提示 Enter/Esc/首节点闭合且数秒后自动隐藏、polygon 顶点直接拖动/删除、顶点拖拽结束不改变 Canvas 视口、边中点插点、双击边界按位置插点、整块 mask 删除、区域合并/去除、布尔选择主区域/扣除区域视觉区分和选择顺序提示、内含去除 hole 渲染、合并模式隐藏编辑手柄、工作区 SAM 提示点点击删除且不冒泡新增点、工作区顶栏撤销/重做按钮、撤销/重做快捷键和输入框快捷键跳过、撤销/重做历史栈 |
| R6 AI 推理 | `src/lib/api.test.ts`, `src/components/CanvasArea.test.tsx`, `src/components/AISegmentation.test.tsx`, `src/components/VideoWorkspace.test.tsx`, `src/components/ModelStatusBadge.test.tsx`, `backend/tests/test_ai.py`, `backend/tests/test_sam2_engine.py` | SAM 2.1 变体选择、点/框/interactive 契约、semantic 禁用、SAM 3 入口隐藏和后端拒绝、SAM 2.1 最高分候选去重、SAM 2.1 框选后正负点细化同一候选 mask、AI 页框选发送 box prompt、AI 页框选后加点发送 interactive prompt、AI 页提示工具上下文提示、AI 页重复执行替换旧候选、SAM 2.1 反向点启用背景过滤且空结果移除旧候选、AI 页不渲染工作区已有 mask、AI 页可在候选 mask 上继续添加正/反点、AI 页可单点删除提示点并删除最近锚点、AI 页可删除选中候选且不删除工作区 mask、AI 页清空只移除本页候选、AI 页参数开关可读性文案且 options 字段不变、AI 页遮罩清晰度只改预览 opacity、AI 页生成 mask 自动选中并可通过分类树换标签、AI 页推送到工作区编辑保留选择和当前帧、SAM 2.1 视频以当前参考帧全部 mask 和起止帧范围自动传播、传播前自动保存 draft/dirty seed mask、传播前独立选择 SAM 2.1 tiny/small/base+/large 权重、自动传播创建 Celery 任务、传播入队权重 id 规范化/拒绝不支持 id、传播 seed 来源 id/签名/边缘平滑 metadata、未编辑传播结果作为 seed 时继承原始签名并跳过重复传播、已编辑传播结果保留 lineage 但重算签名并清理旧结果、中间帧人工新增替代 seed 时清理下游同物体旧传播结果、中间帧 backward 传播清理旧 forward 结果、换权重或换边缘平滑参数传播先清理旧结果、旧临时 seed id 传播结果兼容清理、传播中轮询任务进度、传播任务取消/重试、传播来源 metadata 回显、空提示/空结果反馈、GPU/SAM2.1 状态、AI 参数 options、局部裁剪推理、背景过滤、状态徽标、坐标归一化、正负点 labels、polygons 转 path、后端 fake registry |
| R7 标注保存 | `src/components/VideoWorkspace.test.tsx`, `src/components/CanvasArea.test.tsx`, `src/lib/api.test.ts`, `backend/tests/test_ai.py` | 保存标注、保存后用后端 saved annotation 替换已提交 draft、加载回显、更新 dirty 标注、清空删除已保存标注、GT mask 多类别导入、seed point 回显/归一化、项目不存在、帧不存在 |
| R4 工作区与帧浏览 | `src/components/VideoWorkspace.test.tsx`, `src/components/FrameTimeline.test.tsx` | 加载帧、无帧项目不自动解析并提示生成帧、工作区短状态自动消失、工作区/AI 画布底图默认居中且保留边距、工作区 mask 透明度、回显已保存标注时保留本地未保存 draft mask、选中 mask 后跨帧自动跟随同一传播链结果、清空片段遮罩进入时间轴范围选择并按选区批量清空、清空全部模式、保留人工/AI 模式只清传播 mask、清空人工/AI 标注帧前二次确认、取消确认不删除、仅自动传播帧不确认、清空后裁剪/移除重叠传播历史条、传播权重下拉深色可读配色、缩略图/range/视频处理进度条、视频处理进度条点击跳帧、人工/AI 标注帧红色竖线和标识点击跳帧、自动传播帧通过 source/lineage metadata 识别为蓝色区段和标识点击跳帧、最近自动传播历史片段同一蓝色系按新旧递进纯色显示,旧记录第 5 次后统一阈值色、当前帧白色贯穿线、传播/清空范围洋红/黄绿色边界贯穿线、缩略图红/蓝边框、人工/AI 标注帧叠加传播状态时红框优先保留并显示蓝色内描边、当前人工/AI 标注帧青色外框加红色内描边、普通状态不显示传播范围黄色选区、播放进度条和视频处理进度条选择传播/清空范围、左右方向键切帧、播放、按项目 FPS 显示当前/总时长 |
| R5 工具栏 | `src/components/ToolsPalette.test.tsx`, `src/components/CanvasArea.test.tsx`, `src/components/VideoWorkspace.test.tsx`, `src/store/useStore.test.ts` | 工具切换、工具栏紧凑垂直布局和高度不足时滚动、工具栏低对比滚动条、工具栏外扩滚动条槽位不挤占按钮列、调整多边形工具、AI 跳转、GT Mask 导入位于重叠区域去除之后且使用紫色底色、GT Mask 未知类别导入策略选择、工作区工具栏不展示 AI 正/反点和框选、左侧工具栏不重复撤销/重做、左侧工具栏不展示创建点/创建线段、矩形/圆/多边形手工 mask 绘制、画笔/橡皮擦尺寸控制、画笔新建当前类别 mask、画笔与选中 mask 连通时自动合并、橡皮擦从选中 mask 扣除、未选中 mask 时画布按语义分类树内部优先级渲染、多边形 Enter/首节点闭合、上下文提示提示 Enter/Esc/首节点闭合且数秒后自动隐藏、polygon 顶点直接拖动/删除、顶点拖拽结束不改变 Canvas 视口、边中点插点、双击边界按位置插点、整块 mask 删除、区域合并/去除、布尔选择主区域/扣除区域视觉区分和选择顺序提示、内含去除 hole 渲染、合并模式隐藏编辑手柄、工作区顶栏撤销/重做按钮、顶栏撤销/重做图标强调色、撤销/重做快捷键和输入框快捷键跳过、撤销/重做历史栈 |
| R6 AI 推理 | `src/lib/api.test.ts`, `src/components/CanvasArea.test.tsx`, `src/components/AISegmentation.test.tsx`, `src/components/VideoWorkspace.test.tsx`, `src/components/ModelStatusBadge.test.tsx`, `backend/tests/test_ai.py`, `backend/tests/test_sam2_engine.py` | SAM 2.1 变体选择、点/框/interactive 契约、semantic 禁用、SAM 3 入口隐藏和后端拒绝、SAM 2.1 最高分候选去重、SAM 2.1 框选后正负点细化同一候选 mask、AI 页框选发送 box prompt、AI 页框选后加点发送 interactive prompt、AI 页提示工具上下文提示、AI 页重复执行替换旧候选、SAM 2.1 反向点启用背景过滤且空结果移除旧候选、AI 页不渲染工作区已有 mask、AI 页可在候选 mask 上继续添加正/反点、AI 页可单点删除提示点并删除最近锚点、AI 页可删除选中候选且不删除工作区 mask、AI 页清空只移除本页候选、AI 页参数开关可读性文案且 options 字段不变、AI 页/右侧共享遮罩透明度只改预览 opacity、AI 页生成 mask 自动选中并可通过分类树换标签、AI 页无语义候选禁止推送到工作区并用 error toast 提示、离开 AI 页时清理未分类候选、AI 页推送到工作区编辑保留选择和当前帧、SAM 2.1 视频以当前参考帧全部 mask 和起止帧范围自动传播、传播前自动保存 draft/dirty seed mask、传播前独立选择 SAM 2.1 tiny/small/base+/large 权重、自动传播创建 Celery 任务、传播入队权重 id 规范化/拒绝不支持 id、传播 seed 来源 id/签名和历史平滑 metadata 兼容、历史平滑 seed 保存前对 forward/backward polygon 实际应用边缘平滑并减少密集轮廓点、边缘平滑强度缓入递进曲线、未编辑传播结果作为 seed 时继承原始签名并跳过重复传播、已编辑传播结果保留 lineage 但重算签名并清理旧结果、中间帧人工新增替代 seed 时清理下游同物体旧传播结果、中间帧 backward 传播清理旧 forward 结果、换权重传播先清理旧结果、旧临时 seed id 传播结果兼容清理、传播中轮询任务进度、传播任务取消/重试、传播来源 metadata 回显、空提示/空结果反馈、GPU/SAM2.1 状态、AI 参数 options、局部裁剪推理、背景过滤、状态徽标、坐标归一化、正负点 labels、polygons 转 path、后端 fake registry |
| R7 标注保存 | `src/components/VideoWorkspace.test.tsx`, `src/components/CanvasArea.test.tsx`, `src/lib/api.test.ts`, `backend/tests/test_ai.py` | 保存状态按钮“保存 X 个改动/已全部保存”、保存标注、保存后用后端 saved annotation 替换已提交 draft、加载回显、更新 dirty 标注、清空删除已保存标注、GT mask 多类别导入、seed point 回显/归一化、项目不存在、帧不存在 |
| R8 模板库 | `src/components/TemplateRegistry.test.tsx`, `src/components/TransientNotice.test.tsx`, `src/lib/api.test.ts`, `backend/tests/test_templates.py` | 前端模板加载/新建/编辑/删除、JSON 分类导入、JSON/保存错误非阻塞提示、mapping_rules 解包/打包、后端模板 CRUD |
| R9 本体检查面板 | `src/components/OntologyInspector.test.tsx`, `src/components/CanvasArea.test.tsx`, `src/store/useStore.test.ts`, `backend/tests/test_ai.py` | 模板选择、面板标题简化、面板低对比滚动条、工作区遮罩透明度滑杆、分类展示、具体分类选择、Canvas 选区同步、点击 Canvas mask 后自动聚焦对应语义分类、点击分类给已选 mask 换标签并移动到前端渲染最上层、自定义分类 PATCH 后端模板、目标实例标题显示当前 mask label、隐藏后端模型置信度、选中 mask 后端拓扑属性分析、重新提取拓扑锚点、边缘平滑强度调用后端并将 mask 标记为 dirty |
| R10 Dashboard 与 WebSocket | `src/lib/api.test.ts`, `src/lib/websocket.test.ts`, `src/components/Dashboard.test.tsx`, `backend/tests/test_dashboard.py`, `backend/tests/test_main.py`, `backend/tests/test_progress_events.py`, `backend/tests/test_tasks.py` | 后端概览接口、任务表驱动进度区、最近完成任务保留显示、任务取消/重试/详情、cancelled 事件、Redis 进度事件 payload/发布、地址推导、消息订阅、连接状态回调、队列更新、heartbeat |
| R11 导出 | `src/components/VideoWorkspace.test.tsx`, `src/lib/api.test.ts`, `backend/tests/test_export.py` | COCO/PNG 按钮下载、导出前自动保存、导出路径、JSON 结构、mask ZIP、zIndex 语义融合 |
| R9 本体检查面板 | `src/components/OntologyInspector.test.tsx`, `src/components/CanvasArea.test.tsx`, `src/store/useStore.test.ts`, `backend/tests/test_ai.py` | 模板选择、面板标题简化、面板低对比滚动条、工作区遮罩透明度滑杆、分类展示、具体分类选择、Canvas 选区同步、点击 Canvas mask 后自动聚焦对应语义分类、点击分类给已选 mask 换标签并移动到前端渲染最上层、分类变更同步同一传播链前后帧对应 mask、自定义分类 PATCH 后端模板、目标实例标题显示当前 mask label、隐藏当前选中区域计数、隐藏后端模型置信度、选中 mask 后端拓扑属性分析、拓扑锚点数量按真实 polygon 顶点数显示、分析请求 abort/cancel 静默忽略且旧请求不覆盖新状态、边缘平滑强度防抖预览不标 dirty、应用边缘平滑后将 mask 标记为 dirty、平滑作为实际几何编辑、平滑同步传播链对应 mask、平滑保存时保留传播 lineage 而不把传播帧变成人工/AI 标注帧、平滑撤销/重做、平滑应用后强度归零 |
| R10 Dashboard 与 WebSocket | `src/lib/api.test.ts`, `src/lib/websocket.test.ts`, `src/components/Dashboard.test.tsx`, `backend/tests/test_dashboard.py`, `backend/tests/test_main.py`, `backend/tests/test_progress_events.py`, `backend/tests/test_tasks.py` | 后端概览接口、任务表驱动进度区、最近完成任务保留显示、任务取消/重试/详情、cancelled 事件、Redis 进度事件 payload/发布、地址推导、消息订阅、连接状态回调、队列更新、heartbeat、主动断开不重连 |
| R11 导出 | `src/components/VideoWorkspace.test.tsx`, `src/lib/api.test.ts`, `backend/tests/test_export.py` | 统一分割结果导出下拉、导出前自动保存、整体/范围/当前帧范围参数、特定范围帧可通过播放进度条/视频处理进度条拖拽选择、下载 ZIP 按项目名/`0h00m00s000ms` 起止时间戳/起止项目帧序号命名、导出内容 outputs 参数、Mix_label 透明度参数和预览、兼容 COCO/PNG 路径、JSON 结构、maskid/GT 像素值映射 JSON、原始图片文件夹、按帧/按类别合并的分开 Mask 文件夹、GT_label 黑白图文件夹、Pro_label 彩色图文件夹、Mix_label 原图叠加图文件夹、GT/Pro/Mix 按内部优先级覆盖且和语义分类树顺序一致、GT_label 背景 0、保留类别真实 maskid、导出 GT_label 再导入保持类别一致 |
| R12 配置 | `src/lib/config.test.ts` | env 优先、hostname 推导、WS 推导 |
| R13 文档与测试 | `doc/09-test-plan.md` | 测试覆盖矩阵 |
@@ -32,24 +32,24 @@
| 需求 | 功能点 | 对应测试 | 当前状态 |
|------|--------|----------|----------|
| R1 | 登录页、默认开发凭证、token 写入、失败提示、后端 401 | `Login.test.tsx`, `test_auth.py` | 已覆盖 |
| R2 | 项目列表/创建/选择、视频导入、DICOM 导入、后端项目和帧 CRUD | `ProjectLibrary.test.tsx`, `api.test.ts`, `test_projects.py` | 已覆盖 |
| R1 | 登录页、默认开发管理员、JWT 写入、当前用户写入、刷新恢复基础状态、失败提示、后端 401、`/api/auth/me`、管理员用户管理、角色权限、审计日志、演示出厂设置二次确认、重置后只保留 admin 和未生成帧演示视频项目 | `Login.test.tsx`, `Sidebar.test.tsx`, `UserAdmin.test.tsx`, `useStore.test.ts`, `test_auth.py`, `test_admin.py` | 已覆盖 |
| R2 | 项目列表/创建/选择、项目按用户隔离、视频导入、DICOM 导入、后端项目和帧 CRUD | `ProjectLibrary.test.tsx`, `api.test.ts`, `test_projects.py` | 已覆盖 |
| R3 | 文件类型校验、自动/指定项目上传、视频导入与生成帧分离、显式 FPS 生成帧、项目卡片 FPS 徽标显示 `parse_fps`、视频/DICOM 拆帧任务、非阻塞自动消失操作提示、`parse_fps/max_frames/target_width`、标准帧序列 metadata、任务查询、取消、重试、worker 取消停止 | `ProjectLibrary.test.tsx`, `TransientNotice.test.tsx`, `api.test.ts`, `test_media.py`, `test_tasks.py` | 已覆盖 |
| R4 | 工作区加载帧、无帧项目不自动解析、工作区短状态自动消失、后端标注回显保留本地未保存 draft mask、Canvas/AI 底图居中适配且保留边距、工作区 mask 透明度、选中 mask 后跨帧自动跟随同一传播链结果、清空片段遮罩进入时间轴范围选择并按选区批量清空、传播权重下拉深色可读配色、缩略图/range/视频处理进度条、视频处理进度条点击跳帧、人工/AI 标注帧红色竖线和标识点击跳帧、自动传播帧蓝色区段和标识点击跳帧、最近自动传播历史片段不同色系渐变显示、缩略图红/蓝边框、人工/AI 标注帧叠加传播状态时红框优先保留并显示蓝色内描边、当前人工/AI 标注帧青色外框加红色内描边、普通状态不显示传播范围黄色选区、播放进度条/视频处理进度条拖拽选择传播/清空范围、Canvas/AI 画布拖拽平移回写 position state、当前帧由播放进度条末端和缩略图青色高亮表达/左右方向键切帧、播放、按 FPS 显示时间 | `VideoWorkspace.test.tsx`, `FrameTimeline.test.tsx`, `CanvasArea.test.tsx`, `AISegmentation.test.tsx` | 已覆盖 |
| R5 | 工具切换、工具栏紧凑滚动布局、低对比滚动条、外扩滚动条槽位、调整多边形入口、AI 跳转、矩形/圆/线/点/多边形绘制、已有 mask 上继续绘制、多边形和布尔工具上下文提示、Canvas 上下文提示数秒后自动隐藏 | `ToolsPalette.test.tsx`, `CanvasArea.test.tsx` | 已覆盖 |
| R5 | 顶点直接拖动编辑、顶点拖拽结束不改变 Canvas 视口、边中点插点、双击边界按位置插点、顶点删除、整块删除、工作区 SAM 提示点删除优先级、工作区顶栏撤销/重做按钮、撤销/重做快捷键、区域合并、区域去除、布尔选择主区域黄色实线/扣除区域红色虚线、布尔选择顺序提示、hole even-odd 渲染 | `CanvasArea.test.tsx`, `VideoWorkspace.test.tsx`, `useStore.test.ts` | 已覆盖 |
| R6 | SAM 2.1 变体选择、点/框/interactive、semantic 禁用、SAM 3 入口隐藏和后端拒绝、SAM 2.1 最高分候选去重、AI 页框选/框选后加点、AI 页提示工具上下文提示、AI 页重复执行替换旧候选、AI 页不渲染工作区已有 mask、AI 页可在候选 mask 上继续添加正/反点、AI 页可删除提示点、AI 页可删除选中候选、AI 页清空只移除本页候选、AI 页遮罩清晰度只改预览 opacity、AI 页生成 mask 自动选中并可换标签、AI 页推送到工作区编辑保留选择和当前帧、SAM 2.1 视频按参考帧全部 mask 和范围自动传播、传播前自动保存 draft/dirty seed mask、传播前独立选择 SAM 2.1 tiny/small/base+/large 权重、自动传播 Celery 任务入队、传播入队权重 id 规范化/拒绝不支持 id、传播 seed 来源 id/签名/边缘平滑 metadata、未编辑传播结果作为 seed 时继承原始签名并跳过重复传播、已编辑传播结果保留 lineage 但重算签名并清理旧结果、中间帧人工新增替代 seed 时清理下游同物体旧传播结果、中间帧 backward 传播清理旧 forward 结果、换权重或平滑参数传播先清理旧结果、旧临时 seed id 传播结果兼容清理、前端任务轮询进度、传播任务 runner 保存标注和结果权重 id、传播任务重试、传播空结果提示、GPU/模型状态、参数 options、polygons 转 mask | `api.test.ts`, `CanvasArea.test.tsx`, `AISegmentation.test.tsx`, `VideoWorkspace.test.tsx`, `ModelStatusBadge.test.tsx`, `test_ai.py`, `test_tasks.py`, `test_sam2_engine.py` | 已覆盖 |
| R7 | 保存、保存后替换已提交 draft、查询、更新、删除标注、工作区回显、清空已保存标注、GT mask 导入和 seed point 回写 | `VideoWorkspace.test.tsx`, `CanvasArea.test.tsx`, `api.test.ts`, `test_ai.py` | 已覆盖 |
| R4 | 工作区加载帧、无帧项目不自动解析、工作区短状态自动消失、后端标注回显保留本地未保存 draft mask、Canvas/AI 底图居中适配且保留边距、工作区 mask 透明度、选中 mask 后跨帧自动跟随同一传播链结果、清空片段遮罩进入时间轴范围选择并按选区批量清空、清空全部模式、保留人工/AI 模式只清传播 mask、清空人工/AI 标注帧前二次确认、取消确认不删除、仅自动传播帧不确认、清空后裁剪/移除重叠传播历史条、传播权重下拉深色可读配色、缩略图/range/视频处理进度条、视频处理进度条点击跳帧、人工/AI 标注帧红色竖线和标识点击跳帧、自动传播帧蓝色区段和标识点击跳帧、最近自动传播历史片段同一蓝色系按新旧递进显示,旧记录第 5 次后统一阈值色、当前帧白色贯穿线、传播/清空范围洋红/黄绿色边界贯穿线、缩略图红/蓝边框、人工/AI 标注帧叠加传播状态时红框优先保留并显示蓝色内描边、当前人工/AI 标注帧青色外框加红色内描边、普通状态不显示传播范围黄色选区、播放进度条/视频处理进度条拖拽选择传播/清空范围、Canvas/AI 画布拖拽平移回写 position state、左右方向键切帧、播放、按 FPS 显示时间 | `VideoWorkspace.test.tsx`, `FrameTimeline.test.tsx`, `CanvasArea.test.tsx`, `AISegmentation.test.tsx` | 已覆盖 |
| R5 | 工具切换、工具栏紧凑滚动布局、低对比滚动条、外扩滚动条槽位、调整多边形入口、GT Mask 导入入口位置和紫色底色、工作区工具栏隐藏 AI 正/反点和框选、左侧工具栏不重复撤销/重做、AI 跳转、矩形/圆/线/点/多边形绘制、已有 mask 上继续绘制、多边形和布尔工具上下文提示、Canvas 上下文提示数秒后自动隐藏 | `ToolsPalette.test.tsx`, `CanvasArea.test.tsx` | 已覆盖 |
| R5 | 顶点直接拖动编辑、顶点拖拽结束不改变 Canvas 视口、边中点插点、双击边界按位置插点、顶点删除、整块删除、工作区顶栏撤销/重做按钮、顶栏撤销/重做图标强调色、撤销/重做快捷键、区域合并、区域去除、布尔选择主区域黄色实线/扣除区域红色虚线、布尔选择顺序提示、hole even-odd 渲染 | `CanvasArea.test.tsx`, `VideoWorkspace.test.tsx`, `useStore.test.ts` | 已覆盖 |
| R6 | SAM 2.1 变体选择、点/框/interactive、semantic 禁用、SAM 3 入口隐藏和后端拒绝、SAM 2.1 最高分候选去重、AI 页框选/框选后加点、AI 页提示工具上下文提示、AI 页重复执行替换旧候选、AI 页不渲染工作区已有 mask、AI 页可在候选 mask 上继续添加正/反点、AI 页可删除提示点、AI 页可删除选中候选、AI 页清空只移除本页候选、AI 页/右侧共享遮罩透明度只改预览 opacity、AI 页生成 mask 自动选中并可换标签、AI 页无语义候选禁止推送到工作区并用 error toast 提示、离开 AI 页时清理未分类候选、AI 页推送到工作区编辑保留选择和当前帧、SAM 2.1 视频按参考帧全部 mask 和范围自动传播、传播前自动保存 draft/dirty seed mask、传播前独立选择 SAM 2.1 tiny/small/base+/large 权重、自动传播 Celery 任务入队、传播入队权重 id 规范化/拒绝不支持 id、传播 seed 来源 id/签名和历史平滑 metadata 兼容、历史平滑 seed 保存前对 forward/backward polygon 实际应用边缘平滑并减少密集轮廓点、边缘平滑强度缓入递进曲线、未编辑传播结果作为 seed 时继承原始签名并跳过重复传播、已编辑传播结果保留 lineage 但重算签名并清理旧结果、中间帧人工新增替代 seed 时清理下游同物体旧传播结果、中间帧 backward 传播清理旧 forward 结果、换权重传播先清理旧结果、旧临时 seed id 传播结果兼容清理、前端任务轮询进度、传播任务 runner 保存标注和结果权重 id、传播任务重试、传播空结果提示、GPU/模型状态、参数 options、polygons 转 mask | `api.test.ts`, `CanvasArea.test.tsx`, `AISegmentation.test.tsx`, `VideoWorkspace.test.tsx`, `ModelStatusBadge.test.tsx`, `test_ai.py`, `test_tasks.py`, `test_sam2_engine.py` | 已覆盖 |
| R7 | 保存状态按钮“保存 X 个改动/已全部保存”、保存、保存后替换已提交 draft、查询、更新、删除标注、工作区回显、清空已保存标注、GT mask 导入和 seed point 回写、低数值/16-bit GT_label 图导入、RGB 等通道 maskid 图导入、导入预览、未知 maskid 导入策略、非法彩色 GT mask 拒绝、尺寸不一致自动最近邻拉伸 | `VideoWorkspace.test.tsx`, `CanvasArea.test.tsx`, `api.test.ts`, `test_ai.py` | 已覆盖 |
| R8 | 模板加载、新建、编辑、删除、JSON 分类导入、JSON/保存错误非阻塞提示、mapping_rules 映射、后端 CRUD | `TemplateRegistry.test.tsx`, `TransientNotice.test.tsx`, `api.test.ts`, `test_templates.py` | 已覆盖 |
| R9 | 模板选择、面板标题简化、工作区遮罩透明度滑杆、分类展示、分类选择、点击 mask 自动聚焦对应分类、已选 mask 换标签并置顶显示、自定义分类写入后端模板、目标实例标题显示当前 mask label、隐藏后端模型置信度、后端拓扑属性分析、边缘平滑强度应用、占位状态 | `OntologyInspector.test.tsx`, `CanvasArea.test.tsx`, `useStore.test.ts`, `test_ai.py` | 已覆盖 |
| R9 | 模板选择、面板标题简化、工作区遮罩透明度滑杆、分类展示、分类选择、分类树拖拽调整内部覆盖顺序且不改变 maskid、拖拽后同步同类 mask 层级并标记待保存、点击 mask 自动聚焦对应分类、已选 mask 换标签并置顶显示、分类变更同步同一传播链前后帧对应 mask、自定义分类写入后端模板、目标实例标题显示当前 mask label、隐藏当前选中区域计数、隐藏后端模型置信度、后端拓扑属性分析、拓扑锚点真实顶点计数、分析请求 abort/cancel 静默忽略且旧请求不覆盖新状态、边缘平滑强度防抖预览、边缘平滑应用后确认 dirty、平滑作为实际几何编辑、平滑同步传播链对应 mask、平滑撤销/重做、平滑应用后强度归零、占位状态 | `OntologyInspector.test.tsx`, `CanvasArea.test.tsx`, `useStore.test.ts`, `test_ai.py` | 已覆盖 |
| R10 | Dashboard 概览、任务进度区、最近完成任务保留显示、活动日志、WebSocket progress/complete/error/status/cancelled、取消/重试/详情、连接状态回调、heartbeat | `Dashboard.test.tsx`, `websocket.test.ts`, `test_dashboard.py`, `test_main.py`, `test_progress_events.py`, `test_tasks.py` | 已覆盖 |
| R11 | COCO/PNG ZIP 导出、导出前保存、路径和 JSON/ZIP 结构、zIndex 融合 | `VideoWorkspace.test.tsx`, `api.test.ts`, `test_export.py` | 已覆盖 |
| R11 | 统一“分割结果导出”下拉、整体视频/特定范围帧/当前图片导出、特定范围帧时间轴拖拽选择、ZIP 文件名 `{项目库项目名}_seg_T_{起始时间戳}-{结束时间戳}_P_{起始项目帧序号}-{结束项目帧序号}.zip`、时间戳 `0h00m00s000ms` 格式、项目帧序号使用抽帧后 1-based 顺序、分开 Mask/GT_label/Pro_label/Mix_label outputs、Mix_label 透明度、导出前保存、兼容 COCO/PNG ZIP 路径、JSON/ZIP 结构、maskid/GT 像素值映射、原始图片导出、分开 Mask 按帧子目录与同类合并命名、GT_label/Pro_label/Mix_label 命名、GT/Pro/Mix 内部优先级融合且和语义分类树顺序一致、GT_label 背景 0、保留类别真实 maskid、导出的 GT_label 可按同一模板导回 | `VideoWorkspace.test.tsx`, `api.test.ts`, `test_export.py` | 已覆盖 |
| R12 | API/WS 地址 env 优先和 hostname 推导 | `config.test.ts` | 已覆盖 |
| R13 | 文档测试矩阵与功能点追踪 | `doc/09-test-plan.md` | 已覆盖 |
## 本轮补齐记录
- R5补充 `CanvasArea.test.tsx` 中圆形和线段手工绘制测试,明确验证 metadata、segmentation、bbox/area 和草稿状态
- R5补充 `CanvasArea.test.tsx` 中圆形、画笔新建、画笔连通合并和橡皮擦扣除测试,明确验证 metadata、segmentation、bbox/area、选中状态和草稿状态;补充 `ToolsPalette.test.tsx` 中画笔/橡皮擦尺寸控制测试,并验证创建点、创建线段入口不再显示
- R6补充 `AISegmentation.test.tsx` 中 SAM 2.1 变体选择测试,验证前端不展示 SAM 3 入口、选择 small 后请求携带对应模型,且未放置点提示时不发起推理。
- R6补充 SAM 2 纯文本提示拦截、SAM 2 多候选只保留最高分、SAM 2 engine 单候选请求测试,避免多个重叠候选 mask 被同时叠加。
- R6补充 Canvas 工作区 SAM 2 反向点背景过滤测试,覆盖请求 options 和过滤为空时清除旧候选 mask。
@@ -59,7 +59,9 @@
- R6补充 `propagateMasks()` 同步兼容接口和 `queuePropagationTask()` 任务接口测试,验证当前参考帧全部 mask 会按范围组装为后台传播 steps。
- R6补充 `VideoWorkspace` 自动传播进度测试,验证传播任务运行中显示进度,后端返回 0 个新区域时给出明确反馈。
- R4/R6补充时间轴传播范围选择测试验证点击“自动传播”后可在播放进度条或视频处理进度条上拖拽回填起止帧再提交后台传播任务。
- R4/R6补充视频处理进度条传播历史测试验证多次自动传播后会按不同色系渐变片段显示最近处理范围
- R4/R6补充视频处理进度条传播历史测试验证多次自动传播后会按同一蓝色系显示最近处理范围,最新最亮、旧记录逐次变暗且第 5 次后统一阈值色,单个片段不使用渐变
- R4补充清空片段遮罩后移除重叠传播历史条测试避免已清空视频范围继续显示最近传播进度。
- R4补充清空片段遮罩模式测试覆盖“清空全部”确认删除、“保留人工/AI”只清传播 mask、取消不删除、仅自动传播帧不弹确认。
- R6/R10补充 `queuePropagationTask()``POST /api/ai/propagate/task`、传播 Celery runner 和传播任务重试测试,验证工作区自动传播不再依赖长 HTTP 请求,并验证传给 `SAM2VideoPredictor` 的临时帧文件名是纯数字序列。
- R6补充传播去重回归测试验证前端传播前会先保存 draft seed mask 并用稳定 `source_annotation_id` 入队;后端在 seed 来源由前端临时 id 迁移到后端 annotation id、用户换用其他 SAM 2.1 权重、未编辑传播结果再次作为 seed、已编辑传播结果重新作为 seed、中间帧人工新增替代 seed 时,会分别跳过或清理旧传播标注再保存新结果。
- R6`backend/tests/test_sam3_engine.py` 已标记跳过,仅作为历史保留实现的参考测试,不计入当前产品功能覆盖。
@@ -67,6 +69,8 @@
- R3补充 worker 注册标准帧序列测试,验证帧 `timestamp_ms``source_frame_number``result.frame_sequence` 元数据。
- R8补充 `TemplateRegistry.test.tsx` 中模板编辑、删除测试,验证前端调用真实 API 封装并更新全局 store。
- R9补充 Canvas 选中 mask id 全局同步、本体树点击分类给已选 mask 换标签并移到渲染最上层的测试,验证已保存 mask 会进入 dirty 状态。
- R9补充边缘平滑滑杆防抖测试验证连续拖动只触发最后一次后端预览请求降低拖动卡顿。
- R9补充边缘平滑应用到传播链并可撤销/重做的测试,验证平滑后成为新的实际 polygon、强度归零且不再只保存平滑参数。
## 运行命令

View File

@@ -187,6 +187,11 @@ sam_model_config=configs/sam2.1/sam2.1_hiera_t.yaml
app_env=development
cors_origins=["http://localhost:3000","http://127.0.0.1:3000"]
jwt_secret_key=change-this-to-a-long-random-production-secret
access_token_expire_minutes=1440
default_admin_username=admin
default_admin_password=123456
demo_video_path=/home/wkmgc/Desktop/Seg_Server/Data_MyVideo_1.mp4
EOF
```
@@ -306,6 +311,10 @@ curl http://localhost:9000/minio/health/live
admin / 123456
```
首次启动会自动创建默认管理员,密码以哈希形式写入 `users` 表;登录返回签名 JWT业务接口会校验 `Authorization: Bearer <token>`。生产环境必须修改 `jwt_secret_key` 和默认管理员密码。
默认管理员登录后会看到“用户管理”后台,可新增用户、停用/启用用户、修改角色、重置密码、删除无项目用户并查看登录与用户管理审计日志。角色分为 `admin``annotator``viewer``admin/annotator` 可以执行写入类业务操作,`viewer` 只读。演示部署可在该后台使用“恢复演示出厂设置”,二次确认后只保留默认 admin 和一个尚未生成帧的演示视频项目;该视频来自 `demo_video_path`
---
## 10. 一键启动脚本
@@ -471,7 +480,7 @@ python -m pytest backend/tests
4. 在项目库点击“生成帧”,选择 FPS。
5. Dashboard 中应看到任务进度Celery 日志应显示拆帧任务。
6. 进入分割工作区,能看到帧、时间轴和画布。
7. 手工画一个多边形 mask点击“结构化归档保存”
7. 手工画一个多边形 mask确认顶栏保存状态按钮显示“保存 1 个改动”,点击保存
8. 刷新工作区后,已保存标注应回显。
9. AI 智能分割中选择可用 SAM 2.1 模型,放置点或框,执行分割。
10. 导出 JSON 或 PNG Mask ZIP。