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:
@@ -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 聚类和模板自动映射尚未实现。
|
||||
|
||||
Reference in New Issue
Block a user