feat: 完善工作区交互提示与后端属性分析

功能新增:
- 新增 POST /api/ai/analyze-mask 后端接口,基于 mask polygon、bbox、points 和 score 返回置信度来源、面积、拓扑锚点和后端分析提示。
- 前端新增 analyzeMask API 封装,并在本体检查面板读取选中 mask 的后端几何属性和重新提取拓扑锚点结果。
- 右侧语义分类树点击分类时,会给当前选中 mask 换标签、更新 class 元数据,并将选中 mask 移到前端渲染最上层,方便继续编辑。
- 分割工作区画布新增上下文操作提示,覆盖多边形 Enter 完成、Esc 取消、首节点闭合、拖拽图形、点区域、SAM 点/框提示、区域合并/去除选择顺序和多边形编辑。
- AI 智能分割画布新增正向点、反向点、边界框选和视口控制的上下文提示。
- 自动传播交互收敛为参考帧加起止帧范围加单个“自动传播”按钮,默认使用当前参考帧全部 mask 作为 seed。
- 时间轴改为用浅蓝色进度条区段标记自动传播生成的帧,而不是已编辑帧竖线提示。

Bugfix:
- AI 分割页无当前帧时移除外部演示背景图,改为明确空状态提示,避免误以为外部图片可参与真实推理。
- 工具栏魔法棒文案改为“打开 AI 智能分割”,避免误导为直接触发 SAM 推理。
- Canvas 底部当前图层信息改为显示真实选中 mask 标签和 annotation id,不再使用固定占位文本。
- 已保存标注回显时保留 mask metadata 中的传播来源、score 等字段,供时间轴和属性面板识别。
- 清理 server.ts 中遗留的 /api/login、/api/projects、/api/templates 内存 mock API,避免和 FastAPI 真实后端混淆。

测试:
- 补充 analyze-mask 后端测试,覆盖后端几何属性和锚点返回。
- 补充 api.analyzeMask 前端契约测试,覆盖 normalized polygon、bbox、points 和 extract_skeleton payload。
- 补充本体面板测试,覆盖后端属性读取、自定义分类写回后端模板、选中 mask 换标签和置顶显示。
- 补充 Canvas 测试,覆盖上下文提示、多边形完成提示、布尔选择顺序提示、当前图层真实显示和编辑优先级。
- 补充 AI 分割测试,覆盖无帧空状态和提示工具上下文提示。
- 更新 Konva 测试 mock,支持拖动过程、stroke/dash/fillRule 等渲染断言。

文档:
- 更新 README 和 AGENTS,说明 server.ts 不再保留业务 mock API。
- 更新 doc/02、doc/03、doc/04、doc/05、doc/07、doc/08、doc/09,记录后端属性分析、分类置顶显示、上下文提示、自动传播按钮、传播帧标记、测试覆盖和当前剩余限制。
This commit is contained in:
2026-05-02 02:10:37 +08:00
parent 4c21de02f8
commit b6a276cb8d
28 changed files with 796 additions and 231 deletions

View File

@@ -12,7 +12,7 @@
- FastAPI 后端 API。
- PostgreSQL、MinIO、Redis、SAM 2 等外部基础设施。SAM 3 相关源码保留,但当前产品入口禁用。
开发时前端通过 `server.ts` 启动 Express + Vite middleware后端通过 `backend/main.py` 启动 FastAPI。前端业务接口主要访问 FastAPI不依赖 `server.ts` 中保留的旧 mock API
开发时前端通过 `server.ts` 启动 Express + Vite middleware后端通过 `backend/main.py` 启动 FastAPI。前端业务接口访问 FastAPI`server.ts` 不再保留旧版 `/api/*` mock。
## 前端模块
@@ -30,8 +30,8 @@
| 工作区 | `src/components/VideoWorkspace.tsx` | 加载帧和模板组织工具栏、Canvas、本体面板、时间轴 |
| Canvas | `src/components/CanvasArea.tsx` | 显示帧、缩放平移、点/框提示、渲染 mask |
| 工具栏 | `src/components/ToolsPalette.tsx` | 切换工具、跳转 AI 页面、触发 mask 撤销/重做 |
| 时间轴 | `src/components/FrameTimeline.tsx` | 帧导航、已编辑帧标记、左右方向键切帧、播放和当前/总时长显示 |
| 本体面板 | `src/components/OntologyInspector.tsx` | 模板选择、分类树、本地自定义分类 |
| 时间轴 | `src/components/FrameTimeline.tsx` | 帧导航、自动传播帧浅蓝区段标记、左右方向键切帧、播放和当前/总时长显示 |
| 本体面板 | `src/components/OntologyInspector.tsx` | 模板选择、分类树、后端自定义分类、mask 后端属性分析 |
| AI 页面 | `src/components/AISegmentation.tsx` | 独立 AI 推理视图,使用当前项目帧 |
| 模板库 | `src/components/TemplateRegistry.tsx` | 模板 CRUD、分类编辑、导入、排序 |
@@ -103,7 +103,7 @@
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` 根据当前项目 `frames` 和全局 `masks` 计算有编辑/标注的帧,并在顶部时间进度条上覆盖可点击的琥珀色竖线;当前帧不额外渲染竖线,由播放进度条末端、时间提示和缩略图高亮表达。
6. `FrameTimeline` 根据已保存标注回显到 `Mask.metadata``source` / `propagated_from_frame_id` 计算自动传播生成的帧,并在顶部时间进度条对应帧区段覆盖浅蓝色;当前帧不额外渲染竖线,由播放进度条末端、时间提示和缩略图高亮表达。
7. 当前帧传入 `CanvasArea`
### AI 点/框推理
@@ -125,7 +125,8 @@
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。
18. Canvas 按当前帧过滤并渲染 mask
18. AI 画布左上角根据正向点、反向点、边界框选和视口控制显示上下文提示,说明点击/拖拽、删除提示点和执行推理的操作方式
19. Canvas 按当前帧过滤并渲染 mask。
19. 新 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。
@@ -133,31 +134,32 @@
### 视频片段传播
1. 用户在工作区选择传播对象:`selected` 表示当前帧已选 mask`all` 表示当前帧所有 mask
2. 用户设置起止帧;“传播全部可达”会把起止帧设为 1 到项目总帧数
3. `VideoWorkspace` 以当前帧为 seed将起止帧拆成 `backward` 和/或 `forward` 两段;只包含当前帧时不传播。
1. 用户在工作区打开一帧作为参考帧;该帧全部 mask 都会作为传播 seed不再提供传播对象下拉
2. 用户设置传播起始帧和传播结束帧,并点击唯一的“自动传播”按钮
3. `VideoWorkspace` 以当前参考帧为 seed将起止帧拆成 `backward` 和/或 `forward` 两段;只包含当前帧时不传播。
4. `VideoWorkspace``buildAnnotationPayload()` 把每个 seed mask 转成 normalized polygon、bbox、label、color 和 class 元数据。
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` 记录模型传播来源。
8. 前端传播完成后重新调用 `GET /api/ai/annotations` 并回显新标注。
10. 前端传播完成后重新调用 `GET /api/ai/annotations` 并回显新标注`annotationToMask()` 会保留传播来源 metadata供时间轴浅蓝色进度条区段显示
### 手工绘制与历史栈
1. 用户在 `ToolsPalette` 选择多边形、矩形、圆、点或线工具。
2. `CanvasArea` 将交互坐标转换成像素 polygon。
3. 多边形工具逐次记录节点,三点后点击首节点或按 Enter 时生成闭合 polygon。
4. mask path 只在 `move``edit_polygon``area_merge``area_remove` 工具下拦截点击;绘制和 AI prompt 工具点击已有 mask 时继续冒泡给 Stage
5. mask 写入 `pathData`、像素 `segmentation``bbox``area` 和当前模板分类元数据
6. `addMask()``setMasks()``updateMask()``clearMasks()` 会维护 `maskHistory/maskFuture`
7. 工具栏按钮、AI 页按钮和 Canvas Ctrl+Z/Ctrl+Y 调用 `undoMasks()` / `redoMasks()`
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 页按钮和 Canvas Ctrl+Z/Ctrl+Y 调用 `undoMasks()` / `redoMasks()`
### Polygon 逐点编辑
1. 用户选择“调整多边形”或“拖拽/选择”后点击 Canvas 上的 mask path`CanvasArea` 记录 `selectedMaskId` 并显示该 mask 第一条 polygon 的顶点控制点和边中点插入手柄。
2. 拖动顶点后,前端重算 `pathData`、像素 `segmentation``bbox``area`
2. 顶点 `mousedown/dragstart` 会立即设置当前顶点选择;拖动过程中通过 `dragMove` 实时重算 `pathData`、像素 `segmentation``bbox``area`,不需要先单击顶点再拖动
3. 点击边中点手柄会在该边中点插入新顶点;在“调整多边形”工具下双击 polygon path 会在最接近的线段上按双击位置插入新顶点。
4. 如果 mask 已有 `annotationId`,编辑会把 `saveStatus` 标成 `dirty``saved=false`
5. 归档保存时复用现有 `PATCH /api/ai/annotations/{annotation_id}` 链路,把更新后的 normalized polygon 写回后端。
@@ -168,10 +170,12 @@
1. 用户选择 `area_merge``area_remove` 后,点击多个当前帧 mask 组成选择集。
2. 合并/去除模式隐藏 polygon 顶点和边中点编辑手柄,并在右下角显示已选数量;少于两个 mask 时操作按钮禁用。
3. `CanvasArea``Mask.segmentation` 转为 `polygon-clipping` 的 MultiPolygon
4. `area_merge` 使用 union更新第一个选中的主 mask并从前端 store 移除后续被合并 mask如果被移除 mask 已保存,会调用工作区传入的删除回调删除后端标注
5. `area_remove` 使用 difference从第一个选中的主 mask 中扣除后续选中 mask扣除对象本身保留如果 difference 产生内洞,`segmentation` 保留外圈和 hole ring渲染时使用 even-odd fill
6. 结果会重算 `pathData``segmentation``bbox``area`,已保存主 mask 会进入 dirty 状态并复用归档 PATCH 链路;带洞结果的面积按外圈减内洞计算
3. Canvas 左上角提示布尔选择顺序:第一个选中的是主区域,后续区域参与合并或扣除
4. 布尔选择态按选择顺序区分角色:第一个选中的主区域使用黄色实线轮廓,后续参与合并/扣除的区域使用红色虚线轮廓;所有已选区域填充透明度保持一致,避免被误解为阴影模式异常
5. `CanvasArea``Mask.segmentation` 转为 `polygon-clipping` 的 MultiPolygon
6. `area_merge` 使用 union更新第一个选中的主 mask并从前端 store 移除后续被合并 mask如果被移除 mask 已保存,会调用工作区传入的删除回调删除后端标注
7. `area_remove` 使用 difference从第一个选中的主 mask 中扣除后续选中 mask扣除对象本身保留如果 difference 产生内洞,`segmentation` 保留外圈和 hole ring渲染时使用 even-odd fill。
8. 结果会重算 `pathData``segmentation``bbox``area`,已保存主 mask 会进入 dirty 状态并复用归档 PATCH 链路;带洞结果的面积按外圈减内洞计算。
### GT Mask 导入
@@ -197,7 +201,8 @@
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 被重新分类后进入 `dirty``saved=false`,继续复用工作区归档保存的 PATCH 链路
12. 同一次点击会把这些已选 mask 移动到前端 `masks` 数组末尾;`CanvasArea` 按数组顺序渲染,后渲染的 Path 显示在最上层,方便用户继续编辑刚换标签的区域。该显示置顶不改变模板 `zIndex` 或后端导出语义覆盖规则
13. 已保存 mask 被重新分类后进入 `dirty``saved=false`,继续复用工作区归档保存的 PATCH 链路。
### 导出
@@ -251,5 +256,6 @@
- Dashboard 初始快照来自 `GET /api/dashboard/overview`;任务进度区由 `processing_tasks` queued/running/success/failed/cancelled 任务生成,处理中统计只计算 queued/running。
- 已保存标注支持通过“应用分类”、polygon 顶点拖动/删除、边中点插入、多 polygon 子区域编辑和区域合并/去除进入 dirty 状态并归档更新;选中整块 mask 可用 Delete/Backspace 删除并同步后端;复杂洞结构编辑尚未实现。
- SAM 3 文本语义分割已从当前产品路径中禁用相关源码保留恢复时需要重新接入前端入口、registry、状态接口和测试。
- 自定义分类只存在本地组件状态
- 自定义分类通过 `PATCH /api/templates/{id}` 写入当前激活模板的 `mapping_rules.classes`
- 选中 mask 后,本体面板调用 `POST /api/ai/analyze-mask` 显示后端模型置信度、拓扑锚点数量、面积等属性;“重新提取拓扑锚点”会带 `extract_skeleton=true` 重新请求后端分析。
- GT mask 导入已完成多类别像素值拆分、contour、distance transform seed point 和前端 seed point 拖拽编辑骨架提取、HDBSCAN 聚类和模板自动映射尚未实现。