feat: 完善分割工作区传播与交互闭环

功能增加:新增后端传播任务执行器,支持异步自动传播、传播进度、结果统计、取消/重试状态同步。

功能增加:传播请求支持指定 SAM2.1 tiny/small/base+/large 权重,并记录 seed mask、source annotation 和传播范围。

功能增加:传播逻辑增加 seed 签名,未变化的 mask 二次传播会跳过,已变化的 mask 会先清理旧自动传播结果再重新生成,避免重复重叠。

功能增加:工作区增加传播范围二次选择、传播进度提示、人工/AI 标注帧红色标识、自动传播帧蓝色标识和当前帧双层边框。

功能增加:新增临时提示组件,让工具操作提示自动消失且不阻塞后续操作。

功能增加:补充项目删除、模板删除、任务失败详情、任务取消/重试等前后端联动状态。

功能增加:新增安装部署文档,补充当前需求冻结、设计冻结、接口契约、测试计划和 AGENTS/README 项目说明。

Bugfix:修复自动传播接口 404、传播后看不到任务进度、传播结果重复堆叠和已编辑帧提示不清晰的问题。

Bugfix:修复 AI 分割框选/点选交互、单候选 mask、删除选点、工作区保存与候选 mask 推送相关问题。

Bugfix:修复 Canvas 多边形顶点拖动告警、工具栏提示缺失、项目库 FPS 展示和若干 UI 文案/可用性问题。

测试:补充 AI 分割、Canvas、Dashboard、FrameTimeline、ProjectLibrary、TemplateRegistry、ToolsPalette、VideoWorkspace、API 和后端任务/AI/dashboard 测试。

验证:npm run lint;npm run test:run;python -m pytest backend/tests -q。
This commit is contained in:
2026-05-02 05:17:18 +08:00
parent b6a276cb8d
commit c8c59f7ede
38 changed files with 2852 additions and 212 deletions

View File

@@ -49,16 +49,18 @@
| 导入视频文件 | 真实可用 | 创建项目、上传源视频、刷新项目列表;不会自动拆帧 |
| 生成帧按钮 | 真实可用 | 仅对已导入源视频且尚无帧、非 parsing 状态的项目显示,调用 `parseMedia(projectId, { parseFps })` |
| 生成帧 FPS 滑块 | 真实可用 | 值传入 `/api/media/parse?parse_fps=...`,决定后台拆帧目标 FPS |
| 项目卡片 FPS 徽标 | 真实可用 | 右上角显示关键帧序列目标 `parse_fps`;原始视频帧率只在卡片底部以“原 xx fps”显示 |
| 导入 DICOM 序列 | 部分可用 | 可上传 `.dcm` 并触发解析;体验和错误反馈较粗 |
| 项目状态徽标 | 真实可用 | 项目状态统一为 `pending/parsing/ready/error`,前端兼容归一化旧状态值 |
| 删除项目按钮 | 真实可用 | 点击垃圾桶按钮会确认删除,调用 `DELETE /api/projects/{id}`成功后从项目库移除若删除的是当前项目会清空工作区当前项目、帧、mask 和选区 |
| alert 成功/失败提示 | 真实可用但粗糙 | 使用浏览器 `alert` |
| 操作成功/失败提示 | 真实可用 | 使用非阻塞 `TransientNotice` 浮层,自动消失,不会拦截后续按钮、输入框或画布操作 |
## 工作区 VideoWorkspace
| 元素 | 状态 | 说明 |
|------|------|------|
| 当前项目名 | 真实可用 | 读取 `currentProject.name` |
| 顶栏操作提示 | 真实可用 | 保存、导出、传播范围选择等短反馈会自动消失;保存/导出/传播进行中和无帧项目提示会保留到状态变化 |
| 自动加载项目帧 | 真实可用 | 调用 `GET /api/projects/{id}/frames` |
| 无帧项目提示 | 真实可用 | 如果 `video_path` 存在但无帧,只提示回到项目库生成帧,不自动创建拆帧任务 |
| SAM 模型状态徽标 | 真实可用 | 调用 `GET /api/ai/models/status`,显示当前启用的 SAM 2 与 GPU 状态 |
@@ -66,16 +68,16 @@
| “导出 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用户设置传播起始帧和传播结束帧后,单个“自动传播”按钮会按 seed mask 和前/后方向顺序调用 `POST /api/ai/propagate`,当前启用 SAM 2 video predictor,完成后刷新已保存标注 |
| 参考帧/起止帧/传播权重/自动传播 | 真实可用 | 当前打开帧即参考帧,前端会使用该帧全部 mask 作为 seed工作区顶栏有独立“传播权重”下拉,可在传播前二次选择 SAM 2.1 tiny/small/base+/large 权重,不提供 SAM2/SAM3 家族切换,不影响 AI 智能分割页的单帧推理权重选择;如果用户尚未显式设置范围,点击“自动传播”会先进入时间轴范围选择模式,播放进度条和视频处理进度条都可点击/拖拽回填传播起始帧和传播结束帧,再点击“开始传播”提交;用户也可直接改数字框后点击按钮传播。提交后前端把传播权重 id、seed mask、seed 来源 id 和前/后方向步骤提交到 `POST /api/ai/propagate/task`,后端先规范化/校验权重 id再创建 `processing_tasks` 并由 Celery 执行对应 SAM 2.1 video predictorworker 会按 seed 来源和几何/语义签名做幂等判断,未改变的 seed 直接跳过,已改变的 seed 会先删除同源旧自动传播标注再重新传播,避免重复传播产生重叠 mask传播中顶栏显示任务进度、已处理帧次、删除旧区域数和已保存区域数前端轮询 `GET /api/tasks/{task_id}` 并刷新已保存标注;任务可取消,若完成后 0 个新区域会明确提示没有生成新 mask 或已跳过未改变 mask |
| “结构化归档保存”按钮 | 真实可用 | 未保存 mask 写入 `POST /api/ai/annotate`dirty mask 写入 `PATCH /api/ai/annotations/{id}`;保存成功后会重新拉取后端标注,并用 saved annotation 替换本次提交的 draft mask避免仍显示未保存 |
## CanvasArea 画布
| 元素 | 状态 | 说明 |
|------|------|------|
| 当前帧底图显示 | 真实可用 | `useImage(frameUrl)` 加载当前帧 URL |
| 当前帧底图显示 | 真实可用 | `useImage(frameUrl)` 加载当前帧 URL;切换帧或容器尺寸变化时会按 86% 适配比例居中放大显示,默认留出画布边距,不铺满整个画布 |
| 滚轮缩放 | 真实可用 | 改变 Konva Stage scale |
| 拖拽平移 | 真实可用 | activeTool 为 `move` 时 Stage draggable |
| 拖拽平移 | 真实可用 | activeTool 为 `move` 时 Stage draggable,拖拽结束会回写 React position state避免 Konva 节点位置和前端状态脱节 |
| 光标坐标显示 | 真实可用 | 根据 pointer position 计算 |
| 正向/反向选点 | 真实可用 | UI 能加点,并按当前帧 `frame.id` 调用 `/api/ai/predict`;结果需点击归档保存才持久化 |
| 框选 | 真实可用 | UI 能画框,并把框坐标归一化后调用后端推理;结果需点击归档保存才持久化 |
@@ -100,17 +102,18 @@
| 区域合并/去除 | 真实可用 | 选择工具后点击多个 mask右下角显示已选数量和操作按钮合并/去除模式会隐藏 polygon 编辑手柄,避免手柄抢占多选点击;布尔选择态中第一个选中的主区域用黄色实线轮廓,后续参与合并/扣除的区域用红色虚线轮廓,避免主区域和扣除区域看起来像随机阴影差异;使用 `polygon-clipping` 做 union / difference合并会保留主 mask 并移除被合并 mask去除会从主 mask 扣除后续选中 mask内含扣除会保留 hole ring 并用 even-odd 规则渲染 |
| 正向选点/反向选点/框选 | 部分可用 | 会影响 Canvas 交互,并能触发已对齐的 AI 推理接口;点击工作区内已有 SAM 提示点会优先删除该提示点并重新推理,不会冒泡成新增提示点或 mask 选择 |
| 魔法棒 SAM 触发 | 部分可用 | 切到 AI 页面;不是直接执行推理 |
| 撤销/重做 | 真实可用 | 绑定 Zustand `maskHistory/maskFuture`支持工具栏按钮、AI 页按钮和 Canvas Ctrl+Z/Ctrl+Y |
| 撤销/重做 | 真实可用 | 绑定 Zustand `maskHistory/maskFuture`,支持工作区顶栏按钮、工具栏按钮、AI 页按钮和快捷键 `Ctrl/Cmd+Z``Ctrl/Cmd+Shift+Z``Ctrl/Cmd+Y`;输入框聚焦时不拦截快捷键 |
| 紧凑/滚动布局 | 真实可用 | 工具按钮使用较紧凑的垂直间距;左侧高度不足时工具栏自身出现纵向滚动,不挤压画布 |
## FrameTimeline 时间轴
| 元素 | 状态 | 说明 |
|------|------|------|
| 帧缩略图 | 真实可用 | 使用 `frames[].url` |
| 点击缩略图跳帧 | 真实可用 | 调用 `setCurrentFrame(idx)` |
| 点击缩略图跳帧 | 真实可用 | 调用 `setCurrentFrame(idx)`;非当前帧中,人工/AI 标注帧使用红色边框,自动传播/推理帧使用蓝色边框;当前帧仍用青色外框高亮优先,若当前帧同时是人工/AI 标注帧,则在青色外框内增加红色内描边,避免状态颜色互相覆盖 |
| 顶部 range 拖动 | 真实可用 | 改变当前帧 |
| 具体时间显示 | 真实可用 | 根据项目 `parse_fps/original_fps` 显示当前时间和总时长,格式为 `mm:ss.cc` |
| 自动传播帧进度条标记 | 真实可用 | 根据已保存标注回显的 `mask_data.source` / `propagated_from_frame_id` 识别自动传播生成的帧,并在顶部进度条对应帧区段覆盖浅蓝色;当前帧位置由播放进度条末端、时间提示和缩略图高亮表达 |
| 播放进度条 / 视频处理进度条 | 真实可用 | 播放进度条位于上方,视频处理进度条位于下方;视频处理进度条普通状态下可点击跳转到对应帧;根据已保存标注回显的 `mask_data.source` / `propagated_from_frame_id` 识别自动传播生成的帧并显示蓝色区段,人工绘制或 AI 智能分割生成的帧显示红色竖线,红/蓝标识也可点击跳转到对应帧;未处理背景使用中性灰以和红/蓝标记区分;只有工作区进入自动传播范围选择模式时,两条进度条才显示 amber 选区,并可点击/拖拽选择起止帧 |
| 播放/暂停 | 真实可用 | 当前代码按 `parse_fps/original_fps` 推进帧,最多 30fps |
| 方向键切帧 | 真实可用 | 全局监听左右方向键切到上一帧/下一帧;焦点在 input、textarea、select 或 contentEditable 内时不会拦截 |
@@ -144,6 +147,7 @@
| 删除选中候选 | 真实可用 | 删除 AI 页当前选中的本页候选 mask不会删除工作区已有 maskDelete/Backspace 也遵循同一范围 |
| 清空全体锚点 | 真实可用 | 清空 AI 页提示点和本页生成的候选 mask不删除工作区已有 mask |
| 背景图 / 空状态 | 真实可用 | 优先显示当前项目帧;没有项目帧时显示空状态提示,不再回退到外部演示图片 |
| AI 画布初始视图 | 真实可用 | 当前帧在 AI 画布中默认居中,并按 86% 适配比例尽量放大但保留边距 |
## TemplateRegistry 模板库

View File

@@ -38,7 +38,8 @@ Authorization: Bearer <token>
| `retryTask(taskId)` | `POST /api/tasks/{task_id}/retry` | 对齐 | 对 failed/cancelled 任务创建新的 queued 重试任务 |
| `getProjectFrames(projectId)` | `GET /api/projects/{id}/frames` | 对齐 | 后端返回预签名 image_url以及 `timestamp_ms``source_frame_number` |
| `predictMask(payload)` | `POST /api/ai/predict` | 对齐 | 前端发送 `image_id/prompt_type/prompt_data/model`,并把后端 `polygons` 转为 `masks[].pathData` |
| `propagateMasks(payload)` | `POST /api/ai/propagate` | 对齐 | 当前帧 seed mask 向视频片段传播,并保存后续帧标注 |
| `propagateMasks(payload)` | `POST /api/ai/propagate` | 对齐 | seed 同步传播接口,供后端兼容和测试使用 |
| `queuePropagationTask(payload)` | `POST /api/ai/propagate/task` | 对齐 | 工作区自动传播入口;创建 Celery 后台任务并由任务表/进度流追踪 |
| `getAiModelStatus(selectedModel?)` | `GET /api/ai/models/status` | 对齐 | 返回 GPU 和四个 SAM 2.1 变体状态;`selected_model=sam3` 返回不支持 |
| `analyzeMask(mask, frame, options?)` | `POST /api/ai/analyze-mask` | 对齐 | 后端计算选中 mask 的置信度来源、拓扑锚点数量、面积和 bbox |
| `getProjectAnnotations(projectId, frameId?)` | `GET /api/ai/annotations` | 对齐 | 前端加载工作区时用于回显已保存标注 |
@@ -78,7 +79,8 @@ Authorization: Bearer <token>
| POST | `/api/tasks/{task_id}/cancel` | 取消后台任务 |
| POST | `/api/tasks/{task_id}/retry` | 重试失败或取消的后台任务 |
| POST | `/api/ai/predict` | 当前启用 SAM 2 点/框/interactive 推理 |
| POST | `/api/ai/propagate` | 当前启用 SAM 2 视频片段传播并保存标注 |
| POST | `/api/ai/propagate` | 当前启用 SAM 2 单 seed 同步视频片段传播并保存标注 |
| POST | `/api/ai/propagate/task` | 创建 SAM 2 自动传播后台任务payload 可包含多个 seed/direction step |
| POST | `/api/ai/analyze-mask` | 分析前端选中 mask 的后端几何属性和拓扑锚点 |
| GET | `/api/ai/models/status` | GPU 和 SAM 模型状态 |
| POST | `/api/ai/auto` | 自动分割 |
@@ -230,7 +232,7 @@ SAM 2 点提示和 auto fallback 当前只采用最高分候选 mask避免同
### 视频片段传播请求体
后端接口仍以单个 seed 为单位。工作区前端当前只提供一个“自动传播”按钮:当前打开帧作为参考帧,该帧全部 mask 作为 seed用户设置传播起始帧和传播结束帧后前端会在本地把多个 seed 或前后双向范围拆成多次顺序调用,避免同时启动多个视频 tracker。
`POST /api/ai/propagate` 仍是单 seed 同步接口。工作区实际使用 `POST /api/ai/propagate/task`:当前打开帧作为参考帧,该帧全部 mask 作为 seed用户设置传播起始帧和传播结束帧后前端会在本地把多个 seed 或前后双向范围拆成 `steps`,一次提交为 `propagate_masks` 后台任务,避免长 HTTP 请求和多个视频 tracker 并发抢占 GPU
单次调用示例:
@@ -254,7 +256,31 @@ SAM 2 点提示和 auto fallback 当前只采用最高分候选 mask避免同
}
```
SAM 2.1 变体使用对应 video predictor 的 mask seed 传播;`model=sam2` 会兼容归一化为 tiny`model=sam3` 当前不支持。响应会返回已创建的 `annotations`,保存的 `mask_data.source``<model_id>_propagation`,前端回显时会把该字段保留到 `Mask.metadata`,用于把自动传播帧在时间进度条上标为浅蓝色。
后台任务调用示例:
```json
{
"project_id": 1,
"frame_id": 123,
"model": "sam2.1_hiera_tiny",
"include_source": false,
"save_annotations": true,
"steps": [
{
"direction": "forward",
"max_frames": 30,
"seed": {
"polygons": [[[0.1, 0.1], [0.3, 0.1], [0.3, 0.3]]],
"label": "胆囊",
"color": "#ff0000"
}
}
]
}
```
SAM 2.1 变体使用对应 video predictor 的 mask seed 传播;`model=sam2` 会兼容归一化为 tiny`model=sam3` 当前不支持。响应会返回已创建的 `annotations`,保存的 `mask_data.source``<model_id>_propagation`,前端回显时会把该字段保留到 `Mask.metadata`,用于在视频处理进度条上把自动传播帧显示为蓝色区段。
后台任务入队接口会先规范化/校验 `model` 字段中的 SAM 2.1 权重 id再把规范化后的权重 id 写入 `processing_tasks.payload.model`;前端 seed 会携带 `source_mask_id` 和可用时的 `source_annotation_id`worker 保存传播结果时会写入 `propagation_seed_key``propagation_seed_signature``propagation_direction`。同一 seed、同一权重、同一方向再次传播时如果签名未变化worker 会跳过该 seed如果签名变化worker 会先删除旧自动传播标注再保存新结果。任务运行中/完成后会写入 `processing_tasks.result.model``completed_steps``processed_frame_count``created_annotation_count``deleted_annotation_count``skipped_seed_count` 和每个 step 的权重/方向/数量结果;前端通过 `GET /api/tasks/{task_id}` 轮询Dashboard 同时可通过 Redis/WebSocket 进度流显示该任务。
## 已完成的接口对齐
@@ -270,6 +296,7 @@ SAM 2.1 变体使用对应 video predictor 的 mask seed 传播;`model=sam2`
- `importGtMask()` 已接入 `POST /api/ai/import-gt-mask`,导入后端生成的 polygon 标注、原始 `gt_label_value` 和 seed point。
- `exportMasks()` 已接入 `GET /api/export/{projectId}/masks`
- `parseMedia()` 已改为创建 Celery 后台任务,并返回 `ProcessingTask`
- `queuePropagationTask()` 已接入 `/api/ai/propagate/task`,自动传播不再依赖长时间同步 HTTP 请求。
- `getTask()` 已接入 `GET /api/tasks/{taskId}`
- `cancelTask()` 已接入 `POST /api/tasks/{taskId}/cancel`
- `retryTask()` 已接入 `POST /api/tasks/{taskId}/retry`

View File

@@ -48,10 +48,11 @@
- 若项目有媒体但无帧,工作区只提示需要先在项目库生成帧,不再自动触发拆帧。
- Canvas 显示当前帧图片。
- Canvas 支持滚轮缩放、移动工具拖拽、鼠标坐标显示。
- 时间轴支持缩略图点击切帧、range 拖动切帧、键盘左右方向键切帧、播放/暂停顺序推进帧。
- 时间轴支持缩略图点击切帧、range 拖动切帧、视频处理进度条点击切帧、人工/AI 标注帧和自动传播帧标识点击切帧、键盘左右方向键切帧、播放/暂停顺序推进帧。
- 播放帧率使用项目 `parse_fps``original_fps`,限制在 1 到 30 FPS。
- 时间轴显示当前帧时间和总时长,时间基准使用项目 `parse_fps``original_fps`,格式为 `mm:ss.cc`
- 时间轴根据已保存标注回显的传播来源字段,把自动传播生成的帧在顶部进度条对应区段标为浅蓝色;不再使用竖线模式标记已编辑帧,当前帧位置由播放进度条末端、时间提示和缩略图高亮表达
- 时间轴顶部播放进度条只表达当前播放位置;其下方的视频处理进度条表达处理状态:人工绘制或 AI 智能分割生成的帧显示红色竖线,自动传播生成的帧显示蓝色区段,未处理背景使用中性灰以和标记保持明显区分。底部帧可视化栏中,人工/AI 标注帧缩略图边框为红色,自动传播/推理帧缩略图边框为蓝色,当前帧仍用青色外框高亮优先;如果当前帧同时是人工/AI 标注帧,则显示青色外框加红色内描边
- 自动传播提交前支持独立选择传播权重,范围限定为 SAM 2.1 tiny/small/base+/large 四个权重变体;该选择只影响传播任务,不提供 SAM2/SAM3 家族切换,也不改变 AI 智能分割页的单帧推理权重。
## R5 工具栏
@@ -101,9 +102,11 @@
- 工作区传播功能以当前打开帧作为参考帧,并使用该帧全部 mask 作为 seed用户不再选择“选中区域/当前帧全部”传播对象。
- 工作区传播功能允许设置传播起始帧和传播结束帧;前端以当前参考帧为 seed只向起止范围内位于参考帧之前和之后的帧传播源帧不重复保存。
- 工作区只保留一个“自动传播”按钮,点击后在指定范围内按前向/后向自动生成 mask。
- 前端复用单 seed 后端接口;多个 seed 或双向范围会被拆成多次顺序调用 `POST /api/ai/propagate`避免并发抢占 GPU。
- `POST /api/ai/propagate` 当前支持四个 SAM 2.1 变体;兼容 `model=sam2` 并归一化为 tiny。SAM 2.1 使用官方 `SAM2VideoPredictor.add_new_mask()``propagate_in_video()`
- 传播结果会写入后续帧 `annotations``mask_data.source` 标记为 `<model_id>_propagation`,并保留 label、color 和 class 元数据
- 前端会把多个 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 签名和传播方向。
- 自动传播任务必须避免重复叠加:同一参考 seed、同一权重、同一方向且 seed 签名未变化时worker 直接跳过;同一参考 seed 已变化时worker 先删除对应旧自动传播标注,再保存新传播结果。
- AI 页面会对未放置点提示、后端错误和返回 0 个 mask 的情况显示明确反馈。
- AI 参数支持 `crop_to_prompt``auto_filter_background``min_score`;点/框 prompt 可以裁剪局部区域推理并回映射结果,背景过滤会移除低分结果和包含负向点的 polygon。
- 后端返回 `polygons``scores`

View File

@@ -29,11 +29,13 @@
| 项目库 | `src/components/ProjectLibrary.tsx` | 项目列表、新建、删除、导入视频/DICOM、显式生成帧 |
| 工作区 | `src/components/VideoWorkspace.tsx` | 加载帧和模板组织工具栏、Canvas、本体面板、时间轴 |
| Canvas | `src/components/CanvasArea.tsx` | 显示帧、缩放平移、点/框提示、渲染 mask |
| 工具栏 | `src/components/ToolsPalette.tsx` | 切换工具、跳转 AI 页面、触发 mask 撤销/重做 |
| 时间轴 | `src/components/FrameTimeline.tsx` | 帧导航、自动传播帧浅蓝区段标记、左右方向键切帧、播放和当前/总时长显示 |
| 工具栏 | `src/components/ToolsPalette.tsx` | 切换工具、跳转 AI 页面、触发 mask 撤销/重做;紧凑垂直布局,高度不足时自身滚动 |
| 工作区顶栏 | `src/components/VideoWorkspace.tsx` | 保存/导出/传播/导入 GT、显式撤销/重做按钮和工作区快捷键 |
| 时间轴 | `src/components/FrameTimeline.tsx` | 帧导航、播放进度、视频处理进度条、自动传播范围选择、左右方向键切帧、播放和当前/总时长显示 |
| 本体面板 | `src/components/OntologyInspector.tsx` | 模板选择、分类树、后端自定义分类、mask 后端属性分析 |
| AI 页面 | `src/components/AISegmentation.tsx` | 独立 AI 推理视图,使用当前项目帧 |
| 模板库 | `src/components/TemplateRegistry.tsx` | 模板 CRUD、分类编辑、导入、排序 |
| 短提示浮层 | `src/components/TransientNotice.tsx` | 项目库和模板库的非阻塞成功/失败提示,自动消失 |
## 后端模块
@@ -49,6 +51,7 @@
| Templates | `backend/routers/templates.py` | 模板 CRUD 和 mapping_rules 打包/解包 |
| Media | `backend/routers/media.py` | 上传媒体和拆帧 |
| AI | `backend/routers/ai.py` | 当前启用 SAM 2 推理、视频传播、模型状态和标注保存 |
| 传播任务 | `backend/services/propagation_task_runner.py` | Celery 中执行自动传播 steps写任务进度并保存传播标注 |
| Export | `backend/routers/export.py` | COCO 和 PNG mask 导出 |
| SAM 2 | `backend/services/sam2_engine.py` | SAM 2 懒加载、状态检测、点/框/自动推理和视频 mask 传播 |
| SAM 3 | `backend/services/sam3_engine.py`, `backend/services/sam3_external_worker.py`, `backend/setup_sam3_env.sh` | 历史保留的 SAM 3 桥接源码和脚本;当前未接入 registry |
@@ -85,7 +88,8 @@
5. Celery worker 执行 FFmpeg/OpenCV/pydicom 拆帧,视频帧按 `frame_%06d.jpg``frame_000000.jpg` 连续命名,并按目标宽度缩放。
6. worker 写入 `frames.timestamp_ms``frames.source_frame_number`,并在任务 `result.frame_sequence` 中记录 FPS、帧数、时长、尺寸和对象存储前缀。
7. worker 持续更新 `processing_tasks`,并发布 Redis `seg:progress`
8. 刷新项目列表。
8. 刷新项目列表;项目卡片右上角 FPS 徽标显示生成关键帧序列时选择的 `parse_fps`,原始视频 FPS 仅作为底部“原 xx fps”辅助信息显示
9. 导入视频、生成帧、上传 DICOM 和失败反馈使用 `TransientNotice`,不再使用浏览器 `alert()` 阻塞操作;提示默认数秒后自动消失。
### 任务控制
@@ -103,8 +107,10 @@
3. 帧数据映射为 store `Frame[]`,包含 `timestampMs``sourceFrameNumber`,供时间轴和后续视频传播使用。
4. 工作区调用 `GET /api/ai/annotations` 回显已保存标注时,会替换当前项目帧中的已保存 mask但保留没有 `annotationId` 的未保存 draft mask这保证 AI 页推送到工作区的候选 mask 不会被异步回显覆盖,并会在合并完成后恢复仍然存在的已选 mask id。
5. `CanvasArea` 会把全局 `selectedMaskIds` 中仍存在于当前帧的 id 同步回本地选区,避免帧初始化时的临时清空覆盖 AI 页推送过来的选中态。
6. `FrameTimeline` 根据已保存标注回显到 `Mask.metadata``source` / `propagated_from_frame_id` 计算自动传播生成的帧,并在顶部时间进度条对应帧区段覆盖浅蓝色;当前帧不额外渲染竖线,由播放进度条末端、时间提示和缩略图高亮表达
7. 当前帧传入 `CanvasArea`
6. `CanvasArea` 根据容器和帧尺寸按 86% 适配比例计算初始 scale/position使底图默认居中且尽量大但保留画布边距滚轮缩放和拖拽平移仍由用户后续控制
7. `FrameTimeline` 顶部播放进度条显示当前播放位置;其下方视频处理进度条根据 `Mask.metadata.source` / `propagated_from_frame_id` 计算自动传播帧并显示蓝色区段,对人工绘制或 AI 智能分割等非传播 mask 帧显示红色竖线。普通状态下,视频处理进度条可点击跳转到对应帧,红色人工/AI 标注帧和蓝色自动传播帧标识本身也可点击跳转。处理条未处理背景使用中性灰,和红色/蓝色标记保持明显区分。底部缩略图导航轴对非当前帧使用红色边框标识人工/AI 标注帧,使用蓝色边框标识自动传播/推理帧;当前帧使用青色外框高亮优先,若当前帧同时是人工/AI 标注帧,则以青色外框加红色内描边同时表达两个状态。工作区只有进入自动传播范围选择模式时,播放进度条和视频处理进度条才显示 amber 覆盖层,并可点击/拖拽设置传播起止帧
8. 当前帧传入 `CanvasArea`
9. 工作区顶栏短状态文本会在空闲状态下自动消失;保存、导出、导入 GT 和传播任务运行中仍保留进度状态,无帧项目提示也会保留。
### AI 点/框推理
@@ -126,8 +132,9 @@
16. AI 页面参数开关文案只做展示增强:“局部专注模式(自动裁剪无锚区域)”仍控制 `cropMode/crop_to_prompt`,“严格除杂模式(自动清理干涉点)”仍控制 `autoDeleteBg/auto_filter_background/min_score`
17. AI 页面“遮罩清晰度”滑杆只调节候选 mask 的 Konva preview opacity不写入 `Mask.segmentation`、分类元数据或后端 payload。
18. AI 画布左上角根据正向点、反向点、边界框选和视口控制显示上下文提示,说明点击/拖拽、删除提示点和执行推理的操作方式。
19. Canvas 按当前帧过滤并渲染 mask
19. 新 mask 会带上当前选择的模板分类元数据,包括 `classId``className``classZIndex``metadata.source=ai_segmentation` 和保存状态 `draft`
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 回显替换。
21. 工作区加载项目帧后通过 `GET /api/ai/annotations` 取回已保存标注并转成前端 mask。
22. 工作区“清空遮罩”删除当前帧已保存标注,并清除当前帧本地 mask。
@@ -135,15 +142,17 @@
### 视频片段传播
1. 用户在工作区打开一帧作为参考帧;该帧全部 mask 都会作为传播 seed不再提供传播对象下拉。
2. 用户设置传播起始帧和传播结束帧,并点击唯一的“自动传播”按钮
2. 用户可以直接修改传播起始帧/结束帧数字框,并可通过工作区顶栏“传播权重”下拉独立选择本次传播使用的 SAM 2.1 tiny/small/base+/large 权重;该入口不提供 SAM2/SAM3 家族切换,默认跟随全局 AI 权重,用户手动选择后不再被 AI 页权重切换覆盖
3. `VideoWorkspace` 以当前参考帧为 seed将起止帧拆成 `backward` 和/或 `forward` 两段;只包含当前帧时不传播。
4. `VideoWorkspace``buildAnnotationPayload()` 把每个 seed mask 转成 normalized polygon、bbox、label、colorclass 元数据。
5. 前端每个 seed、每个方向顺序调用 `POST /api/ai/propagate``include_source=false``save_annotations=true`顺序调用是为了避免多个视频 tracker 并发抢占 GPU
6. 后端按项目帧序列截取片段,下载对应帧到临时 `frame_%06d.jpg` 目录,保持当前帧在片段中的相对索引
7. `model` 为任一 SAM 2.1 变体时,`sam2_engine` 使用对应 checkpoint/config 加载 `SAM2VideoPredictor.add_new_mask()` 注入 seed mask再用 `propagate_in_video()` 传播。
8. `model=sam3` 当前不支持SAM 3 video tracker 代码保留但没有接入产品路径
9. 后端把传播返回的 normalized polygon 保存为后续帧 `Annotation`,跳过源帧,`mask_data.source` 记录模型传播来源
10. 前端传播完成后重新调用 `GET /api/ai/annotations` 并回显新标注;`annotationToMask()` 会保留传播来源 metadata供时间轴浅蓝色进度条区段显示
4. `VideoWorkspace``buildAnnotationPayload()` 把每个 seed mask 转成 normalized polygon、bbox、label、colorclass 元数据`source_mask_id` 和可用时的 `source_annotation_id`
5. 前端把传播权重 id、每个 seed、每个方向组装成 `steps`,一次调用 `POST /api/ai/propagate/task``include_source=false``save_annotations=true`接口先规范化/校验 `model` 字段中的权重 id再创建 `processing_tasks.task_type=propagate_masks` 并投递 Celery避免长 HTTP 请求阻塞前端等待
6. `VideoWorkspace` 记录返回的 `task_id`,轮询 `GET /api/tasks/{task_id}` 显示任务 message、步骤进度、已处理帧次和已保存区域数任务运行期间提供取消传播按钮调用通用 `POST /api/tasks/{task_id}/cancel`
7. Celery worker 逐 step 顺序执行传播,避免多个视频 tracker 并发抢占 GPU每个 step 开始/完成都会写入 `processing_tasks.progress/result/message` 并发布 Redis `seg:progress`Dashboard 可同步显示。每个 step 开始前worker 会用 seed 来源 id、规范化权重 id、传播方向和 seed 签名查找旧传播标注:签名相同则跳过该 seed签名不同则先删除对应方向的旧自动传播标注再执行新的 video predictor 传播。
8. 后端按项目帧序列截取片段,下载对应帧到临时目录,并写成 `000000.jpg` 这类纯数字文件名;这是 `SAM2VideoPredictor` 对视频帧排序的要求,和项目库中持久化的 `frame_%06d.jpg` 对象名无关
9. `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 调用内部暂不提供逐帧流式进度
10. `model=sam3` 当前不支持SAM 3 video tracker 代码保留但没有接入产品路径
11. 后端把传播返回的 normalized polygon 保存为后续帧 `Annotation`,跳过源帧,`mask_data.source` 记录权重传播来源,同时写入 `propagation_seed_key``propagation_seed_signature``propagation_direction``source_annotation_id``source_mask_id` 供后续幂等传播判断。
12. 前端轮询到已创建区域后刷新 `GET /api/ai/annotations` 并回显新标注;任务结束后如果后端返回 0 个新区域,工作区会明确提示没有生成新的 mask若是未改变 seed 被跳过则提示未改变 mask 已跳过。`annotationToMask()` 会保留传播来源 metadata供时间轴视频处理进度条显示蓝色传播区段。
### 手工绘制与历史栈
@@ -154,7 +163,7 @@
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 页按钮和 Canvas Ctrl+Z/Ctrl+Y 调用 `undoMasks()` / `redoMasks()`
8. 工具栏按钮、工作区顶栏按钮和 AI 页按钮调用 `undoMasks()` / `redoMasks()`;工作区由 `VideoWorkspace` 统一处理 `Ctrl/Cmd+Z``Ctrl/Cmd+Shift+Z``Ctrl/Cmd+Y`,并在输入框、下拉框和可编辑文本聚焦时跳过快捷键,避免影响帧范围输入
### Polygon 逐点编辑
@@ -203,6 +212,7 @@
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` 非阻塞提示,默认数秒后自动消失。
### 导出
@@ -222,7 +232,8 @@
- `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`
- `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。
- `getProjectAnnotations()` 使用 `GET /api/ai/annotations`
@@ -235,7 +246,7 @@
- SAM 2.1 点提示和 auto fallback 只返回一个最高分候选,避免同一提示产生多个重叠候选 mask。
- SAM 3 前端入口、后端 registry 入口和状态展示均已禁用;`model=sam3` 会返回不支持。
- 后端 `/api/ai/predict` 支持可选 `options``crop_to_prompt` 会对 point/box/interactive prompt 做局部裁剪推理并回映射 polygon`auto_filter_background` 会按 `min_score` 和负向点过滤结果。
- 后端 `/api/ai/propagate` 当前支持所选 SAM 2.1 mask seed 视频传播;当前前端默认向后传播 30 帧并保存结果标注
- 后端 `/api/ai/propagate/task` 当前支持所选 SAM 2.1 mask seed 视频传播后台任务;同步 `/api/ai/propagate` 仍保留为单 seed 兼容接口
- 后端 `/api/ai/models/status` 返回 GPU 和四个 SAM 2.1 变体的真实运行状态。
- point prompt 支持旧数组形式和 `{ points, labels }` 对象形式。

View File

@@ -16,12 +16,12 @@
|------|----------|--------|
| 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、删除级联、帧列表 |
| R3 媒体上传与拆帧 | `src/components/ProjectLibrary.test.tsx`, `backend/tests/test_media.py`, `backend/tests/test_tasks.py` | 视频导入不自动拆帧、显式生成帧 FPS 选择、扩展名校验、自动建项目、关联项目、创建异步任务、标准帧序列参数、帧时间戳/源帧号、任务序列元数据、worker 注册帧、取消任务、重试任务、取消后 worker 停止 |
| R4 工作区与帧浏览 | `src/components/VideoWorkspace.test.tsx`, `src/components/FrameTimeline.test.tsx` | 加载帧、无帧项目不自动解析并提示生成帧、回显已保存标注时保留本地未保存 draft mask、缩略图/range/自动传播帧浅蓝进度条区段标记、当前帧由进度条末端和缩略图高亮表达/左右方向键切帧、播放、按项目 FPS 显示当前/总时长 |
| R5 工具栏 | `src/components/ToolsPalette.test.tsx`, `src/components/CanvasArea.test.tsx`, `src/store/useStore.test.ts` | 工具切换、调整多边形工具、AI 跳转、矩形/圆/线/点/多边形手工 mask 绘制、点工具在已有 mask 上落点、多边形 Enter/首节点闭合、上下文提示提示 Enter/Esc/首节点闭合、polygon 顶点直接拖动/删除、边中点插点、双击边界按位置插点、整块 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 和起止帧范围自动传播、传播来源 metadata 回显、空提示/空结果反馈、GPU/SAM2.1 状态、AI 参数 options、局部裁剪推理、背景过滤、状态徽标、坐标归一化、正负点 labels、polygons 转 path、后端 fake registry |
| 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 画布底图默认居中且保留边距、回显已保存标注时保留本地未保存 draft mask、缩略图/range/视频处理进度条、视频处理进度条点击跳帧、人工/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 顶点直接拖动/删除、边中点插点、双击边界按位置插点、整块 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 和起止帧范围自动传播、传播前独立选择 SAM 2.1 tiny/small/base+/large 权重、自动传播创建 Celery 任务、传播入队权重 id 规范化/拒绝不支持 id、传播 seed 来源 id/签名 metadata、未改变 seed 跳过、已改变 seed 先删旧自动传播标注再重传、传播中轮询任务进度、传播任务取消/重试、传播来源 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 回显/归一化、项目不存在、帧不存在 |
| R8 模板库 | `src/components/TemplateRegistry.test.tsx`, `src/lib/api.test.ts`, `backend/tests/test_templates.py` | 前端模板加载/新建/编辑/删除、JSON 分类导入、mapping_rules 解包/打包、后端模板 CRUD |
| 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 选区同步、点击分类给已选 mask 换标签并移动到前端渲染最上层、自定义分类 PATCH 后端模板、选中 mask 后端属性分析、重新提取拓扑锚点 |
| 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 语义融合 |
@@ -34,13 +34,13 @@
|------|--------|----------|----------|
| R1 | 登录页、默认开发凭证、token 写入、失败提示、后端 401 | `Login.test.tsx`, `test_auth.py` | 已覆盖 |
| R2 | 项目列表/创建/选择、视频导入、DICOM 导入、后端项目和帧 CRUD | `ProjectLibrary.test.tsx`, `api.test.ts`, `test_projects.py` | 已覆盖 |
| R3 | 文件类型校验、自动/指定项目上传、视频导入与生成帧分离、显式 FPS 生成帧、视频/DICOM 拆帧任务、`parse_fps/max_frames/target_width`、标准帧序列 metadata、任务查询、取消、重试、worker 取消停止 | `ProjectLibrary.test.tsx`, `test_media.py`, `test_tasks.py` | 已覆盖 |
| R4 | 工作区加载帧、无帧项目不自动解析、后端标注回显保留本地未保存 draft mask、Canvas 底图、缩略图/range/自动传播帧浅蓝进度条区段标记、当前帧由进度条末端和缩略图高亮表达/左右方向键切帧、播放、按 FPS 显示时间 | `VideoWorkspace.test.tsx`, `FrameTimeline.test.tsx`, `CanvasArea.test.tsx` | 已覆盖 |
| R5 | 工具切换、调整多边形入口、AI 跳转、矩形/圆/线/点/多边形绘制、已有 mask 上继续绘制、多边形和布尔工具上下文提示 | `ToolsPalette.test.tsx`, `CanvasArea.test.tsx` | 已覆盖 |
| R5 | 顶点直接拖动编辑、边中点插点、双击边界按位置插点、顶点删除、整块删除、工作区 SAM 提示点删除优先级、撤销/重做、区域合并、区域去除、布尔选择主区域黄色实线/扣除区域红色虚线、布尔选择顺序提示、hole even-odd 渲染 | `CanvasArea.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 和范围自动传播、GPU/模型状态、参数 options、polygons 转 mask | `api.test.ts`, `CanvasArea.test.tsx`, `AISegmentation.test.tsx`, `VideoWorkspace.test.tsx`, `ModelStatusBadge.test.tsx`, `test_ai.py`, `test_sam2_engine.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 底图居中适配且保留边距、缩略图/range/视频处理进度条、视频处理进度条点击跳帧、人工/AI 标注帧红色竖线和标识点击跳帧、自动传播帧蓝色区段和标识点击跳帧、缩略图红/蓝边框、当前人工/AI 标注帧青色外框加红色内描边、普通状态不显示传播范围黄色选区、播放进度条/视频处理进度条拖拽选择传播范围、Canvas/AI 画布拖拽平移回写 position state、当前帧由播放进度条末端和缩略图青色高亮表达/左右方向键切帧、播放、按 FPS 显示时间 | `VideoWorkspace.test.tsx`, `FrameTimeline.test.tsx`, `CanvasArea.test.tsx`, `AISegmentation.test.tsx` | 已覆盖 |
| R5 | 工具切换、工具栏紧凑滚动布局、调整多边形入口、AI 跳转、矩形/圆/线/点/多边形绘制、已有 mask 上继续绘制、多边形和布尔工具上下文提示 | `ToolsPalette.test.tsx`, `CanvasArea.test.tsx` | 已覆盖 |
| R5 | 顶点直接拖动编辑、边中点插点、双击边界按位置插点、顶点删除、整块删除、工作区 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 和范围自动传播、传播前独立选择 SAM 2.1 tiny/small/base+/large 权重、自动传播 Celery 任务入队、传播入队权重 id 规范化/拒绝不支持 id、传播 seed 来源 id/签名 metadata、未改变 seed 跳过、已改变 seed 先删旧自动传播标注再重传、前端任务轮询进度、传播任务 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` | 已覆盖 |
| R8 | 模板加载、新建、编辑、删除、JSON 分类导入、mapping_rules 映射、后端 CRUD | `TemplateRegistry.test.tsx`, `api.test.ts`, `test_templates.py` | 已覆盖 |
| R8 | 模板加载、新建、编辑、删除、JSON 分类导入、JSON/保存错误非阻塞提示、mapping_rules 映射、后端 CRUD | `TemplateRegistry.test.tsx`, `TransientNotice.test.tsx`, `api.test.ts`, `test_templates.py` | 已覆盖 |
| R9 | 模板选择、分类展示、分类选择、已选 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` | 已覆盖 |
@@ -56,7 +56,10 @@
- R6补充 `ModelStatusBadge.test.tsx` 中 SAM 3 不展示测试,避免禁用入口重新出现在前端。
- R6补充后端 `selected_model=sam3` 拒绝测试和 semantic 禁用测试,避免后端继续暴露 SAM 3 产品能力。
- R6补充 `POST /api/ai/propagate` 后端测试,验证 seed mask 传播结果会保存为后续帧标注并保留 class 元数据。
- R6补充 `propagateMasks()` API 封装和 `VideoWorkspace` 自动传播按钮测试,验证当前参考帧全部 mask 会按范围发送到后端视频传播接口
- R6补充 `propagateMasks()` 同步兼容接口和 `queuePropagationTask()` 任务接口测试,验证当前参考帧全部 mask 会按范围组装为后台传播 steps
- R6补充 `VideoWorkspace` 自动传播进度测试,验证传播任务运行中显示进度,后端返回 0 个新区域时给出明确反馈。
- R4/R6补充时间轴传播范围选择测试验证点击“自动传播”后可在播放进度条或视频处理进度条上拖拽回填起止帧再提交后台传播任务。
- R6/R10补充 `queuePropagationTask()``POST /api/ai/propagate/task`、传播 Celery runner 和传播任务重试测试,验证工作区自动传播不再依赖长 HTTP 请求,并验证传给 `SAM2VideoPredictor` 的临时帧文件名是纯数字序列。
- R6`backend/tests/test_sam3_engine.py` 已标记跳过,仅作为历史保留实现的参考测试,不计入当前产品功能覆盖。
- R3补充 `parseMedia()` 查询参数和后端拆帧任务 payload 测试,验证 `parse_fps``max_frames``target_width` 会进入任务。
- R3补充 worker 注册标准帧序列测试,验证帧 `timestamp_ms``source_frame_number``result.frame_sequence` 元数据。

490
doc/10-installation.md Normal file
View File

@@ -0,0 +1,490 @@
# Installation / 部署安装指南
本文件记录当前仓库的真实安装和部署方式。它面向一台新的 Linux 机器,目标是跑起完整系统:
- React 前端:默认 `http://localhost:3000`
- FastAPI 后端:默认 `http://localhost:8000`
- PostgreSQL项目、帧、模板、标注、任务元数据
- RedisCelery broker/result backend 与进度 pub/sub
- MinIO视频、DICOM、拆帧图片等对象存储
- Celery worker执行视频/DICOM 拆帧等后台任务
- SAM 2.1:当前产品启用 tiny/small/base+/largeSAM 3 源码保留但产品入口禁用,正常部署不需要安装 SAM 3
---
## 1. 前置条件
推荐环境:
| 项 | 建议 |
|----|------|
| OS | Ubuntu 22.04 LTS 或相近 Linux |
| Python | 3.11 |
| Node.js | 22.x |
| 数据库 | PostgreSQL 14+ |
| 缓存/队列 | Redis 6+ |
| 对象存储 | MinIO |
| 视频处理 | FFmpeg |
| GPU | NVIDIA GPU + CUDA用于 SAM 2.1 推理;无 GPU 时可 CPU 运行但会很慢 |
安装系统依赖:
```bash
sudo apt update
sudo apt install -y \
postgresql postgresql-contrib \
redis-server \
ffmpeg \
libpq-dev build-essential curl ca-certificates gnupg wget
```
安装 MinIO
```bash
cd /tmp
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/
mkdir -p ~/minio_data
```
---
## 2. 获取代码
```bash
cd ~/Desktop
git clone <your-gitea-or-git-url> Seg_Server
cd Seg_Server
```
如果已经有仓库,进入项目根目录即可:
```bash
cd /home/wkmgc/Desktop/Seg_Server
```
后续命令默认在项目根目录执行,除非特别说明。
---
## 3. 配置 PostgreSQL
默认后端配置来自 `backend/config.py`
```text
postgresql://seguser:segpass123@localhost:5432/segserver
```
创建数据库和用户:
```bash
sudo systemctl start postgresql
sudo -u postgres psql -c "CREATE DATABASE segserver;"
sudo -u postgres psql -c "CREATE USER seguser WITH PASSWORD 'segpass123';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE segserver TO seguser;"
sudo -u postgres psql -d segserver -c "GRANT ALL ON SCHEMA public TO seguser;"
sudo -u postgres psql -c "ALTER DATABASE segserver OWNER TO seguser;"
```
验收:
```bash
pg_isready
psql "postgresql://seguser:segpass123@localhost:5432/segserver" -c "select 1;"
```
---
## 4. 启动 Redis 和 MinIO
Redis
```bash
sudo systemctl start redis-server
redis-cli ping
```
MinIO
```bash
nohup minio server ~/minio_data --console-address :9001 > /tmp/minio.log 2>&1 &
curl http://localhost:9000/minio/health/live
```
默认 MinIO 账号密码是:
```text
minioadmin / minioadmin
```
后端启动时会检查并创建 bucket
```text
seg-media
```
---
## 5. 安装后端 Python 环境
推荐使用 Conda
```bash
conda create -n seg_server python=3.11 -y
conda activate seg_server
```
安装 PyTorch。根据机器 CUDA 版本选择合适 wheel。示例
```bash
# CUDA 12.4 示例
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
# 无 GPU / CPU 示例
# pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
```
安装后端依赖:
```bash
cd backend
pip install -r requirements.txt
cd ..
```
确认关键包:
```bash
python - <<'PY'
import fastapi, sqlalchemy, redis, celery, minio, torch
print("torch:", torch.__version__, "cuda:", torch.cuda.is_available())
PY
```
---
## 6. 配置后端环境变量
后端从 `backend/.env` 读取配置;该文件被 `.gitignore` 忽略,不要提交真实密码或本机路径。
创建 `backend/.env`
```bash
cat > backend/.env <<'EOF'
db_url=postgresql://seguser:segpass123@localhost:5432/segserver
redis_url=redis://localhost:6379/0
minio_endpoint=localhost:9000
minio_access_key=minioadmin
minio_secret_key=minioadmin
minio_secure=false
sam_default_model=sam2.1_hiera_tiny
sam_model_path=/home/wkmgc/Desktop/Seg_Server/models/sam2.1_hiera_tiny.pt
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"]
EOF
```
如果前端通过局域网 IP 访问,例如 `http://192.168.3.11:3000`,需要把该地址加入 `cors_origins`,同时前端也要配置 API 地址。
---
## 7. 准备 SAM 2.1 权重
当前产品入口只暴露 SAM 2.1 变体:
- `sam2.1_hiera_tiny`
- `sam2.1_hiera_small`
- `sam2.1_hiera_base_plus`
- `sam2.1_hiera_large`
下载脚本:
```bash
cd backend
python download_sam2.py
cd ..
```
脚本默认下载到:
```text
/home/wkmgc/Desktop/Seg_Server/models/
```
推荐文件名:
```text
models/sam2.1_hiera_tiny.pt
models/sam2.1_hiera_small.pt
models/sam2.1_hiera_base_plus.pt
models/sam2.1_hiera_large.pt
```
可以只部署 tiny前端会显示四个选项但只有本地存在 checkpoint 的模型会显示可用。
注意SAM 3 相关脚本和源码是历史保留。当前前端入口隐藏 SAM 3后端 registry 不暴露 `sam3`,正常部署不需要下载 SAM 3 权重,也不要把 Hugging Face token 写进项目文件。
---
## 8. 安装前端依赖
```bash
npm install
```
如需指定前端访问的后端地址,在项目根目录创建 `.env`
```bash
cat > .env <<'EOF'
VITE_API_BASE_URL=http://localhost:8000
VITE_WS_PROGRESS_URL=ws://localhost:8000/ws/progress
EOF
```
如果不设置,前端会按当前浏览器 hostname 推导:
```text
http://<browser-host>:8000
ws://<browser-host>:8000/ws/progress
```
---
## 9. 手动启动所有服务
开 4 个终端分别启动。
终端 AFastAPI 后端
```bash
conda activate seg_server
cd /home/wkmgc/Desktop/Seg_Server/backend
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
```
终端 BCelery worker
```bash
conda activate seg_server
cd /home/wkmgc/Desktop/Seg_Server/backend
celery -A celery_app:celery_app worker --loglevel=info --concurrency=1
```
终端 C前端开发服务
```bash
cd /home/wkmgc/Desktop/Seg_Server
npm run dev
```
终端 D确认基础设施
```bash
pg_isready
redis-cli ping
curl http://localhost:9000/minio/health/live
```
访问:
| 服务 | 地址 |
|------|------|
| 前端 | `http://localhost:3000` |
| FastAPI Docs | `http://localhost:8000/docs` |
| Health | `http://localhost:8000/health` |
| MinIO Console | `http://localhost:9001` |
默认开发登录:
```text
admin / 123456
```
---
## 10. 一键启动脚本
项目根目录有 `start_services.sh`
```bash
chmod +x start_services.sh
./start_services.sh
```
脚本会检查/启动:
```text
PostgreSQL -> Redis -> MinIO -> FastAPI -> Celery worker -> 前端
```
使用前必须检查脚本里的本机路径和 sudo 逻辑:
- `PROJECT_DIR="/home/wkmgc/Desktop/Seg_Server"`
- `CONDA_ENV="seg_server"`
- MinIO 数据目录 `/home/wkmgc/minio_data`
- 脚本里包含本机 sudo 密码写法,迁移机器时应移除或改成安全的 systemd/service 管理方式
---
## 11. 生产构建方式
前端构建:
```bash
npm run build
```
生产模式启动前端静态服务:
```bash
NODE_ENV=production npm start
```
后端生产启动示例:
```bash
cd backend
uvicorn main:app --host 0.0.0.0 --port 8000
```
Celery worker 仍需要单独启动:
```bash
cd backend
celery -A celery_app:celery_app worker --loglevel=info --concurrency=1
```
实际生产建议用 systemd、supervisor 或容器编排托管 FastAPI、Celery、前端静态服务、MinIO、Redis、PostgreSQL。
---
## 12. 部署验收 Checklist
基础服务:
```bash
pg_isready
redis-cli ping
curl http://localhost:9000/minio/health/live
curl http://localhost:8000/health
```
后端模型状态:
```bash
curl http://localhost:8000/api/ai/models/status
```
前端质量检查:
```bash
npm run lint
npm run test:run
npm run build
```
后端测试:
```bash
conda activate seg_server
python -m pytest backend/tests
```
手工业务验收:
1. 打开 `http://localhost:3000`
2. 使用 `admin / 123456` 登录。
3. 创建项目或上传视频。
4. 在项目库点击“生成帧”,选择 FPS。
5. Dashboard 中应看到任务进度Celery 日志应显示拆帧任务。
6. 进入分割工作区,能看到帧、时间轴和画布。
7. 手工画一个多边形 mask点击“结构化归档保存”。
8. 刷新工作区后,已保存标注应回显。
9. AI 智能分割中选择可用 SAM 2.1 模型,放置点或框,执行分割。
10. 导出 JSON 或 PNG Mask ZIP。
---
## 13. 常见问题
### 前端打不开或请求后端失败
检查:
```bash
curl http://localhost:8000/health
cat .env
```
如果通过局域网 IP 访问前端,确保:
- `.env``VITE_API_BASE_URL` 是浏览器可访问的后端地址。
- `backend/.env``cors_origins` 包含前端地址。
### Dashboard WebSocket 经常断开
检查:
```bash
redis-cli ping
curl http://localhost:8000/health
```
同时确认前端 `VITE_WS_PROGRESS_URL` 指向真实可访问的:
```text
ws://<host>:8000/ws/progress
```
### 生成帧没有进度
检查 Celery worker 是否启动:
```bash
ps aux | grep celery
tail -f /tmp/celery.log
```
检查 Redis
```bash
redis-cli ping
```
### MinIO 上传失败
检查:
```bash
curl http://localhost:9000/minio/health/live
tail -f /tmp/minio.log
```
如果磁盘空间不足MinIO 可能拒绝写入。清理 `~/minio_data`、旧日志、旧模型权重或迁移数据目录。
### SAM 2 模型不可用
检查:
```bash
ls -lh models/
curl http://localhost:8000/api/ai/models/status
```
常见原因:
- checkpoint 文件不存在。
- `backend/.env``sam_model_path` 指向旧文件名。
- `sam2` Python 包未正确安装。
- PyTorch/CUDA 不匹配。
### 不需要 SAM 3
当前版本不用 SAM 3。不要为了正常部署执行 `backend/setup_sam3_env.sh`,也不要在项目里保存 Hugging Face token。

View File

@@ -20,6 +20,7 @@
| [07-current-requirements-freeze.md](./07-current-requirements-freeze.md) | 当前版本需求冻结,测试以此为准 |
| [08-current-design-freeze.md](./08-current-design-freeze.md) | 当前版本设计冻结,记录模块、数据流和接口边界 |
| [09-test-plan.md](./09-test-plan.md) | 需求到测试文件的覆盖矩阵和运行命令 |
| [10-installation.md](./10-installation.md) | 系统安装部署指南,覆盖 PostgreSQL、Redis、MinIO、后端、Celery、前端和 SAM 2.1 权重 |
## 状态标记