功能增加:点击 Canvas mask 后,右侧语义分类树会按 classId/className/label 自动匹配分类,并滚动聚焦到对应分类按钮。
功能增加:工作区新增按起止帧批量清空片段遮罩,复用传播范围输入,范围内已保存标注走 DELETE /api/ai/annotations/{id},本地 draft mask 同步移除。
功能增加:右侧语义分类树上方新增工作区 mask 透明度滑杆,写入 Zustand maskPreviewOpacity,Canvas mask 预览按该值渲染并保留选中加亮反馈。
功能增加:视频处理进度条记录最近自动传播区间,使用不同色系深浅渐变提示最近处理片段。
功能增加:工作区自动传播前会先保存 draft/dirty seed mask,使用稳定后端 source_annotation_id 入队,减少二次传播重复结果。
Bugfix:后端传播任务对旧临时 seed id、不同 SAM 2.1 权重结果做兼容清理;相同 seed 和相同权重才跳过,否则先删旧自动传播标注再重传。
Bugfix:修复 polygon 顶点拖拽结束后触发 Stage 平移导致画布中心偏移的问题,并补充测试环境对 drag target 的模拟。
Bugfix:工具提示会在数秒后自动隐藏,避免创建多边形/矩形等提示长期遮挡画布。
UI 调整:移除右侧面板顶部‘本体论与属性分类管理树’说明栏,减少无效占位。
UI 调整:左侧工具栏和右侧语义面板使用低对比 seg-scrollbar;左侧工具栏外扩滚动条槽位,避免滚动条挤占图标列。
UI 调整:工作区模型状态徽标改为紧凑显示,减少与传播权重选择重复;传播权重下拉改成深色背景和青色文字,避免灰底白字不可读。
UI 调整:缩略图状态框固定优先级,当前帧、人工/AI 标注帧、自动传播帧可用外框/内框组合同时表达。
测试:补充 VideoWorkspace、CanvasArea、FrameTimeline、OntologyInspector、ToolsPalette、useStore 和后端 test_ai 覆盖新增交互、传播去重、批量清空、透明度、滚动条和 UI 状态。
文档:同步更新 README、AGENTS 和 doc/03、doc/04、doc/07、doc/08、doc/09,记录当前功能、接口契约、需求设计冻结和测试覆盖。
186 lines
18 KiB
Markdown
186 lines
18 KiB
Markdown
# 当前需求冻结文档
|
||
|
||
冻结日期:2026-05-01
|
||
|
||
本文档描述当前仓库已经实现或明确保留为占位的需求。测试用例以本文档为准,不把早期设想或 Word 文档中的远期能力当作当前版本必须实现的功能。
|
||
|
||
## R1 登录与会话
|
||
|
||
- 系统提供登录页。
|
||
- 默认开发凭证为 `admin / 123456`。
|
||
- 登录成功后前端保存 token,并进入主应用。
|
||
- 登录失败时显示错误信息。
|
||
- 当前 token 是开发用固定 token,不做真实 JWT 校验。
|
||
|
||
## R2 项目管理
|
||
|
||
- 前端展示项目库,并从 `GET /api/projects` 获取项目列表。
|
||
- 用户可以新建项目,前端调用 `POST /api/projects`。
|
||
- 用户可以选择项目,进入工作区。
|
||
- 用户可以导入视频文件,前端创建项目、上传文件并刷新项目列表;导入视频不自动拆帧。
|
||
- 用户可以对已导入且尚未生成帧的视频项目点击“生成帧”,在弹窗中选择目标 FPS 后创建拆帧任务。
|
||
- 用户可以导入 DICOM 序列,前端上传 DICOM、触发拆帧、刷新项目列表。
|
||
- 用户可以在项目卡片上删除项目;前端调用 `DELETE /api/projects/{id}`,删除成功后从项目库移除,若删除当前项目则清空工作区当前项目、帧、mask 和选区。
|
||
- 后端支持项目创建、列表、详情、局部更新和删除。
|
||
- 后端删除项目时通过 ORM 级联删除项目帧、标注、导出 mask 元数据和后台任务记录。
|
||
- 后端支持项目帧创建、列表和单帧查询。
|
||
|
||
## R3 媒体上传与拆帧
|
||
|
||
- 后端允许上传视频、图片、DICOM 文件,其他扩展名返回 400。
|
||
- 未提供项目 ID 上传时,后端自动创建项目。
|
||
- 提供项目 ID 上传时,后端把上传对象关联到该项目。
|
||
- 拆帧接口根据项目 `source_type` 处理视频或 DICOM。
|
||
- 拆帧接口支持 `parse_fps`、`max_frames` 和 `target_width` 参数,用于生成可被 SAM 2 视频处理复用的标准帧序列。
|
||
- 视频帧使用连续 `frame_%06d.jpg` 命名,默认从 `frame_000000.jpg` 开始,并按 `target_width` 缩放。
|
||
- 拆帧完成后写入 `frames` 记录,并把项目状态设为 `ready`。
|
||
- 每条帧记录包含 `frame_index`、`image_url`、`width`、`height`、`timestamp_ms` 和 `source_frame_number`。
|
||
- 任务完成结果包含 `frame_sequence` 元数据:`original_fps`、`parse_fps`、`frame_count`、`duration_ms`、`target_width`、帧宽高和对象存储前缀。
|
||
- 拆帧接口会创建 `processing_tasks` 记录并投递 Celery worker。
|
||
- 前端可通过 `GET /api/tasks/{task_id}` 查询任务状态。
|
||
- 后端支持 `POST /api/tasks/{task_id}/cancel` 取消 queued/running 任务,写入 `cancelled` 状态并尝试 revoke Celery。
|
||
- 后端支持 `POST /api/tasks/{task_id}/retry` 对 failed/cancelled 任务创建新的 queued 任务。
|
||
- worker 会在关键阶段检查任务是否已取消,取消后停止继续写帧。
|
||
|
||
## R4 工作区与帧浏览
|
||
|
||
- 工作区根据当前项目加载帧列表。
|
||
- 若项目有媒体但无帧,工作区只提示需要先在项目库生成帧,不再自动触发拆帧。
|
||
- Canvas 显示当前帧图片。
|
||
- Canvas 支持滚轮缩放、移动工具拖拽、鼠标坐标显示。
|
||
- 时间轴支持缩略图点击切帧、range 拖动切帧、视频处理进度条点击切帧、人工/AI 标注帧和自动传播帧标识点击切帧、键盘左右方向键切帧、播放/暂停顺序推进帧。
|
||
- 播放帧率使用项目 `parse_fps` 或 `original_fps`,限制在 1 到 30 FPS。
|
||
- 时间轴显示当前帧时间和总时长,时间基准使用项目 `parse_fps` 或 `original_fps`,格式为 `mm:ss.cc`。
|
||
- 时间轴顶部播放进度条只表达当前播放位置;其下方的视频处理进度条表达处理状态:人工绘制或 AI 智能分割生成的帧显示红色竖线,自动传播生成的帧显示蓝色区段,最近自动传播处理过的片段叠加不同色系的横向渐变条,片段内部随时间从深到浅,帮助识别最近处理范围;未处理背景使用中性灰以和标记保持明显区分。底部帧可视化栏中,人工/AI 标注帧缩略图边框为红色,自动传播/推理帧缩略图边框为蓝色,当前帧仍用青色外框高亮优先;如果同一帧既有人工/AI 标注又有自动传播结果,红色人工/AI 标注框优先保留,自动传播状态只作为蓝色内描边或次级提示;如果当前帧同时是人工/AI 标注帧,则显示青色外框加红色内描边,外层选中框和内层标注框顺序不能交换。
|
||
- 自动传播提交前支持独立选择传播权重,范围限定为 SAM 2.1 tiny/small/base+/large 四个权重变体;该选择只影响传播任务,不提供 SAM2/SAM3 家族切换,也不改变 AI 智能分割页的单帧推理权重。
|
||
|
||
## R5 工具栏
|
||
|
||
- 工具栏可以切换当前 active tool。
|
||
- 正向点、反向点、框选工具会影响 Canvas 交互。
|
||
- 魔法棒按钮切换到 AI 页面。
|
||
- 多边形、矩形、圆、点、线工具会在 Canvas 上生成可保存的 polygon mask。
|
||
- 多边形通过点击取点并按 Enter 完成,也支持三点后点击首节点闭合;矩形、圆、线通过拖拽生成;点工具生成小点区域。
|
||
- 创建多边形、创建矩形、区域合并/去除、调整多边形等 Canvas 左上角上下文提示只作为短提示,切换工具或操作状态变化时显示,数秒后自动隐藏,避免长期遮挡待编辑图像;再次切换工具或操作状态变化会重新显示。
|
||
- 绘制工具点击已有 mask 时应继续执行当前绘制动作,不应被 mask 选择逻辑吞掉。
|
||
- 工具栏提供“调整多边形”工具,用户可以点击 mask 进入 polygon 顶点编辑态;按住顶点即可直接拖动并实时更新 mask 几何,不需要先单击选中顶点,已保存 mask 会标记为 dirty;顶点和 seed point 等子节点拖拽不能冒泡成画布拖拽,编辑结束后 Canvas 当前缩放和平移视口必须保持不变。
|
||
- 顶点编辑态显示边中点插入手柄;点击边中点会在该边中间新增顶点。
|
||
- “调整多边形”工具下双击 polygon 边界时,会在最接近的线段上按双击位置新增顶点。
|
||
- 顶点编辑态下选中顶点后可用 Delete/Backspace 删除顶点,但不会让 polygon 少于三点。
|
||
- 选中整个 mask 且未选中具体顶点时,Delete/Backspace 删除该 mask;已保存 mask 同步调用后端删除接口。
|
||
- 撤销、重做绑定全局 `maskHistory/maskFuture`,支持工具栏按钮、AI 页按钮和 Canvas 快捷键。
|
||
- 区域合并工具支持多选当前帧 mask,并使用 polygon union 生成合并后的主 mask。
|
||
- 区域去除工具支持多选当前帧 mask,并从第一个选中的主 mask 中扣除后续选中 mask。
|
||
- 区域合并/去除模式显示已选数量,并隐藏 polygon 编辑手柄以避免手柄抢占多选点击;第一个选中的主区域使用黄色实线轮廓,后续参与合并/扣除的区域使用红色虚线轮廓。
|
||
- 区域去除结果包含内洞时,前端保留 hole ring 并用 even-odd 规则渲染。
|
||
|
||
## R6 AI 推理
|
||
|
||
- 当前 AI 页面支持选择 `sam2.1_hiera_tiny`、`sam2.1_hiera_small`、`sam2.1_hiera_base_plus`、`sam2.1_hiera_large`;SAM 3 选择、文本输入和相关状态展示已隐藏。
|
||
- 前端和工作区通过 `GET /api/ai/models/status` 展示 GPU 和四个 SAM 2.1 变体的真实运行状态;`selected_model=sam3` 会被后端拒绝。
|
||
- 前端 `predictMask()` 调用 `POST /api/ai/predict`。
|
||
- 前端发送后端契约:`image_id`、`prompt_type`、`prompt_data`、`model`。
|
||
- 点提示传 `{ points, labels }`,正向点 label 为 1,反向点 label 为 0。
|
||
- AI 页面在已有候选 mask 上点击正向/反向选点时,应继续添加提示点,不应被 mask 选择事件拦截。
|
||
- AI 页面点击已有提示点应删除对应点;“删除最近锚点”只移除最近放置的提示点,不删除候选 mask 或工作区 mask。
|
||
- AI 页面“删除选中候选”和 Delete/Backspace 只删除本页生成且已选中的 AI 候选 mask,不删除工作区已有 mask。
|
||
- 工作区点击已有 SAM 提示点应优先删除该点并按剩余提示重新推理;该事件不得冒泡成新增提示点、mask 选择或其它画布点击行为。
|
||
- 框选提示传归一化 `[x1, y1, x2, y2]`。
|
||
- AI 页面边界框选应支持画布拖拽预览框;执行分割时只框选不加点发送 `box` prompt,框选后继续加点发送 `interactive` prompt。
|
||
- 工作区 SAM 2.1 框选会建立一个候选 mask;后续正向点/反向点会携带原始框和累计点,以 `interactive` prompt 细化并替换同一个候选 mask。
|
||
- 工作区 SAM 2.1 一旦包含反向点,会随请求启用 `auto_filter_background` 和 `min_score=0.05`;若后端判定反向点排除了当前候选区域并返回空结果,前端会移除旧候选 mask,避免继续显示已被否定的区域。
|
||
- SAM 2.1 不支持文本语义提示;当前 AI 页面不提供文本语义输入,必须使用点/框提示。
|
||
- SAM 2.1 点提示和 auto fallback 默认只采用一个最高分候选 mask,避免多个候选 mask 作为同一结果重叠显示。
|
||
- AI 页面只渲染本页最新生成的候选 mask;重复执行高精度分割会替换上一次 AI 页候选,工作区已有手工、保存、传播或 GT 导入 mask 不会自动进入 AI 画布,也不会被替换。
|
||
- AI 页面提供“遮罩清晰度”滑杆,调节本页候选 mask 的预览透明度,不改变 mask 几何、分类或保存数据。
|
||
- AI 页面参数开关展示文案使用“局部专注模式(自动裁剪无锚区域)”和“严格除杂模式(自动清理干涉点)”;这是 UI 可读性文案,不改变 `cropMode`、`autoDeleteBg` 或后端 `options` 字段。
|
||
- AI 页面生成的 SAM 2.1 mask 会写入全局 `masks`,自动同步到当前项目帧,并写入全局 `selectedMaskIds`;右侧语义分类树可以直接给新生成 mask 换标签。
|
||
- AI 页“清空全体锚点”只清空本页提示点和本页生成的候选 mask,不删除工作区已有 mask。
|
||
- AI 页面“推送至工作区编辑”会切回工作区并把工具切到“调整多边形”,保留当前选中的 AI mask 以便继续编辑轮廓和归档保存。
|
||
- 工作区加载后端已保存标注时,必须保留当前项目帧里尚未保存的 AI/手工 draft mask,避免 AI 页推送到工作区的候选 mask 被异步回显流程覆盖。
|
||
- 语义文本提示 `semantic` 当前被后端禁用并返回 400。
|
||
- SAM 3 源码和历史测试保留,但不属于当前产品可用功能;前端不再展示 SAM 3 入口,后端 registry 不暴露 `sam3`。
|
||
- 工作区传播功能以当前打开帧作为参考帧,并使用该帧全部 mask 作为 seed;用户不再选择“选中区域/当前帧全部”传播对象。
|
||
- 工作区传播功能允许设置传播起始帧和传播结束帧;前端以当前参考帧为 seed,只向起止范围内位于参考帧之前和之后的帧传播,源帧不重复保存。
|
||
- 工作区只保留一个“自动传播”按钮,点击后在指定范围内按前向/后向自动生成 mask。
|
||
- 自动传播提交前,前端必须先保存当前项目中的 draft/dirty mask;参考帧 seed 优先使用后端 `annotation_id` 作为稳定来源,避免第一次用前端临时 id 传播、后续保存后无法替换旧传播结果。
|
||
- 前端会把多个 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 已变化或用户改用其他 SAM 2.1 权重时,worker 先删除对应旧自动传播标注,再保存新传播结果;对早期只记录前端临时 `source_mask_id` 的旧传播结果,worker 会按同一参考帧、传播方向和语义信息做兼容清理。
|
||
- 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` 中,点击“结构化归档保存”后持久化到后端标注表。
|
||
|
||
## R7 标注保存
|
||
|
||
- 后端提供 `POST /api/ai/annotate` 保存标注。
|
||
- 保存时必须存在项目;如果传入 `frame_id`,帧也必须存在。
|
||
- 后端提供 `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。
|
||
- 保存成功后,前端会重新拉取后端标注,并用后端 saved annotation 替换本次提交的 draft mask;未提交的其他 draft mask 仍保留。
|
||
- 工作区“清空遮罩”会删除当前帧已保存标注,并清空当前帧未保存 mask。
|
||
- 工作区加载项目帧后会查询已保存标注并回显。
|
||
- 工作区支持导入 GT mask 图片,前端调用 `POST /api/ai/import-gt-mask`。
|
||
- 后端导入 GT mask 时按非零像素值拆分多类别区域,再按连通域生成 polygon 标注,并通过距离变换写入 seed point。
|
||
- 前端会回显导入标注的 seed point;拖动 seed point 后,已保存标注会变为 dirty,归档保存时会更新后端 `points`。
|
||
|
||
## R8 模板库
|
||
|
||
- 前端展示模板列表,调用 `GET /api/templates`。
|
||
- 用户可以新建、编辑、删除模板。
|
||
- 模板分类存放在 `mapping_rules.classes`,规则存放在 `mapping_rules.rules`。
|
||
- 前端支持添加/删除分类、拖拽排序后重算 `zIndex`、JSON 批量导入、加载腹腔镜默认分类。
|
||
- 后端支持模板创建、列表、详情、局部更新和删除。
|
||
|
||
## R9 本体检查面板
|
||
|
||
- 工作区右侧可以选择模板。
|
||
- 面板显示模板分类;新增自定义分类会写入当前激活模板的后端 `mapping_rules.classes`。
|
||
- 用户可以选择具体分类;新 AI mask 会记录 `classId`、`className`、`classZIndex`,并在保存时写入 `mask_data.class`。
|
||
- 如果 Canvas 当前已经选中一个或多个 mask,点击语义分类树会把这些 mask 的 `label`、`color` 和 class 元数据改为该分类;已保存 mask 会进入 `dirty` 状态,归档保存时更新后端。
|
||
- 添加自定义分类需要先选择模板,保存时调用 `PATCH /api/templates/{id}` 并同步全局模板 store。
|
||
- 选中 mask 后,置信度、拓扑锚点和重新提取拓扑锚点按钮调用 `POST /api/ai/analyze-mask`,不再显示固定占位值。
|
||
|
||
## R10 Dashboard 与 WebSocket
|
||
|
||
- Dashboard 显示基础统计、任务进度和活动日志。
|
||
- Dashboard 初始数据来自 `GET /api/dashboard/overview`。
|
||
- 后端聚合项目数、处理中任务数、标注数、帧数、模板数和主机 load average。
|
||
- 任务进度由 `processing_tasks` 中的 queued/running/success/failed/cancelled 任务生成,避免刚完成任务从进度区立即消失;处理中任务数统计只计算 queued/running;活动日志由最近任务、项目、标注和模板记录生成。
|
||
- Dashboard 对 queued/running 任务提供取消按钮,对 failed/cancelled 任务提供重试按钮。
|
||
- Dashboard 任务详情会读取 `GET /api/tasks/{task_id}` 并展示失败 error、payload、result、Celery ID 和时间信息。
|
||
- Dashboard 会连接 `/ws/progress`。
|
||
- 收到 progress、complete、error、status 消息时,前端会更新队列或日志。
|
||
- 收到 cancelled 消息时,前端会把对应任务标记为已取消。
|
||
- Celery worker 每次更新 `processing_tasks` 后会发布 Redis `seg:progress` 事件,FastAPI 订阅并广播给 `/ws/progress` 客户端。
|
||
- 后端 WebSocket 接收到客户端消息后返回 status heartbeat。
|
||
|
||
## R11 导出
|
||
|
||
- 后端支持 `GET /api/export/{project_id}/coco` 导出 COCO JSON。
|
||
- 后端支持 `GET /api/export/{project_id}/masks` 导出 PNG mask ZIP。
|
||
- 当前前端 `exportCoco()` API 封装已对齐后端路径。
|
||
- 当前前端 `exportMasks()` API 封装已对齐后端路径。
|
||
- 工作区“导出 JSON 标注集”按钮已绑定下载事件;导出前会先保存当前未归档 mask。
|
||
- 工作区“导出 PNG Mask ZIP”按钮已绑定下载事件;导出前会先保存当前未归档 mask。
|
||
- PNG mask ZIP 包含单标注二值 mask、按 zIndex 融合后的每帧语义 mask 和 `semantic_classes.json`。
|
||
|
||
## R12 配置
|
||
|
||
- 前端 API 地址由 `src/lib/config.ts` 统一推导。
|
||
- `VITE_API_BASE_URL` 优先级高于自动推导。
|
||
- `VITE_WS_PROGRESS_URL` 优先级高于从 API 地址推导 WebSocket 地址。
|
||
- 未设置环境变量时,前端按当前浏览器 hostname 推导 `http://<host>:8000`。
|
||
|
||
## R13 文档与测试
|
||
|
||
- `doc/` 目录保存当前实现审计、接口契约、需求冻结、设计冻结和测试计划。
|
||
- 测试应覆盖当前冻结需求中的真实功能、半可用行为和明确占位行为。
|
||
- 对外部服务依赖 PostgreSQL、MinIO、Redis、SAM 模型的测试应使用 mock 或测试替身,不依赖真实服务可用性。
|