完善遮罩删除范围选择

- 删除/清空已保存标注前预检后端 annotation id,跳过本地陈旧 id,避免重复 DELETE 产生 404 控制台红字

- 左侧工具栏新增 DEL 删除选中遮罩入口,调整清空遮罩弹窗文案为“清空所有传播帧”,并加入按帧范围选择入口

- 区域合并和重叠区域去除在存在传播帧时弹出当前帧/所有传播帧选择,传播帧同步后保留原 lineage metadata

- 多 polygon 或分离区域组成的 mask 选中后显示全部顶点与插点手柄,同帧传播链分散 mask 点选时联动高亮

- 调整工具栏分组分隔线位置,只在清空遮罩下方保留 tool-group-separator 测试标记

- 更新 VideoWorkspace、CanvasArea、ToolsPalette 回归测试和相关项目文档
This commit is contained in:
2026-05-03 22:14:00 +08:00
parent 275be62db5
commit 5ae1d15336
12 changed files with 215 additions and 55 deletions

View File

@@ -158,7 +158,7 @@
21. 新 mask 会带上当前选择的模板分类元数据,包括 `classId``className``classZIndex``metadata.source=ai_segmentation` 和保存状态 `draft`
20. 顶栏保存状态按钮按当前项目待保存数量显示为“保存 X 个改动”或“已全部保存”;用户点击保存后,前端将像素 `segmentation` 转成 normalized `mask_data.polygons`;未保存 mask 调用 `POST /api/ai/annotate`dirty mask 会先读取当前后端标注 id 列表,已知存在的 id 调用 `PATCH /api/ai/annotations/{annotation_id}`,已知缺失的本地旧 id 直接保留同一 `mask_data`、几何、分类和传播 lineage metadata 改用 `POST /api/ai/annotate` 重新创建;如果预检后发生并发删除导致 `PATCH` 返回 404也会降级为重新创建并在随后回显时排除本地旧 mask id保存成功后本次提交的 draft mask id 会从本地保留列表中排除,并由后端 saved annotation 回显替换。
21. 工作区加载项目帧后通过 `GET /api/ai/annotations` 取回已保存标注并转成前端 mask。
22. 工作区“清空遮罩”只从左侧工具栏触发;如果当前帧存在选中 mask则以当前帧选中 mask 为清空对象,否则以当前帧全部 mask 为清空对象。如果清空对象没有关联其它传播帧,直接删除当前帧已保存标注并清除当前帧本地 mask不弹确认如果存在传播链结果`VideoWorkspace` 弹出范围选择,用户可选择只清当前帧、清空当前帧及同传播链所有自动传播帧,或取消。本操作不删除其它帧独立 AI 推理或人工 mask。
22. 工作区“清空遮罩”只从左侧工具栏触发;如果当前帧存在选中 mask则以当前帧选中 mask 为清空对象,否则以当前帧全部 mask 为清空对象。如果清空对象没有关联其它传播帧,直接删除当前帧已保存标注并清除当前帧本地 mask不弹确认如果存在传播链结果`VideoWorkspace` 弹出范围选择,用户可选择只清当前帧、清空当前帧及同传播链所有自动传播帧、进入按帧范围选择,或取消。本操作不删除其它帧独立 AI 推理或人工 mask。左侧工具栏的 `DEL` 按钮和键盘 Delete/Backspace 删除整块 mask 时复用同一传播链范围确认;删除已保存标注前会通过 `GET /api/ai/annotations` 预检当前项目仍存在的 annotation id只对存在的 id 发送 `DELETE`
### 视频片段传播
@@ -190,14 +190,14 @@
### Polygon 逐点编辑
1. 用户选择“调整多边形”或“拖拽/选择”后点击 Canvas 上的 mask path`CanvasArea` 记录 `selectedMaskId` 并显示该 mask 第一条 polygon 的顶点控制点和边中点插入手柄。
1. 用户选择“调整多边形”或“拖拽/选择”后点击 Canvas 上的 mask path`CanvasArea` 记录 `selectedMaskId` 并显示该 mask 所有可编辑 polygon 的顶点控制点和边中点插入手柄;多 polygon 或分离区域组成的同一个 mask 不再只显示第一条 polygon
2. 顶点 `mousedown/dragstart` 会立即设置当前顶点选择;拖动过程中通过 `dragMove` 实时重算 `pathData`、像素 `segmentation``bbox``area`,不需要先单击顶点再拖动。
3. Stage 的 `onDragEnd` 只处理 Stage 自身拖拽polygon 顶点等子节点拖拽结束事件会被忽略,避免子节点坐标误写入 Canvas `position` 导致视口跳动。
4. 点击边中点手柄会在该边中点插入新顶点;在“调整多边形”工具下双击 polygon path 会在最接近的线段上按双击位置插入新顶点。
5. 如果 mask 已有 `annotationId`,编辑会把 `saveStatus` 标成 `dirty``saved=false`
6. 归档保存时复用现有 `PATCH /api/ai/annotations/{annotation_id}` 链路,把更新后的 normalized polygon 写回后端。
7. 选中顶点后 Delete/Backspace 可删除顶点;前端保持 polygon 至少三点。
8. 未选中具体顶点但选中了 mask 时Delete/Backspace 从前端 store 删除该 mask如果包含 `annotationId`,通过工作区回调调用后端删除接口;删除对象属于传播链或传播 seed 时,删除范围会扩展到同链自动传播 mask但不移除其他帧独立 AI 推理/人工 mask。
8. 未选中具体顶点但选中了 mask 时Delete/Backspace 从前端 store 删除该 mask左侧工具栏 `DEL` 按钮调用同一删除逻辑。如果包含 `annotationId`,通过工作区回调先预检后端 annotation id 再调用删除接口;删除对象属于传播链或传播 seed 时,删除范围会扩展到同链自动传播 mask但不移除其他帧独立 AI 推理/人工 mask。
9. 普通 mask 和导入 mask 都不显示黄色 seed point也不提供 seed point 拖动;保存 payload 仍可保留已有 `points` 数据兼容,但画布体验统一为区域选择和 polygon 顶点编辑。
### 区域合并与去除
@@ -207,8 +207,8 @@
3. Canvas 左上角提示布尔选择顺序:第一个选中的是主区域,后续区域参与合并或扣除。
4. 布尔选择态按选择顺序区分角色:第一个选中的主区域使用黄色实线轮廓,后续参与合并/扣除的区域使用红色虚线轮廓;所有已选区域填充透明度保持一致,避免被误解为阴影模式异常。
5. `CanvasArea``Mask.segmentation` 转为 `polygon-clipping` 的 MultiPolygon。
6. `area_merge` 使用 union更新第一个选中的主 mask并从前端 store 移除后续被合并 mask如果被移除 mask 已保存,会调用工作区传入的删除回调删除后端标注。执行会按 `source_annotation_id``source_mask_id``propagation_seed_key` 查找其它帧中与当前主区域/参与区域对应的传播 mask在每个具备对应关系的帧上执行同一次 union只删除该帧参与合并的次级 mask避免把同链但未参与同步的区域整链误删。
7. `area_remove` 使用 difference从第一个选中的主 mask 中扣除后续选中 mask扣除对象本身保留同样会在其它传播帧中找到对应主区域和扣除区域并执行 difference扣除区域本身继续保留如果 difference 产生内洞,`segmentation` 保留外圈和 hole ring`metadata.polygonRingCounts` 记录每个 polygon 的 ring 数,渲染时使用 even-odd fill。
6. `area_merge` 使用 union更新第一个选中的主 mask并从前端 store 移除后续被合并 mask如果被移除 mask 已保存,会调用工作区传入的删除回调删除后端标注。执行会按 `source_annotation_id``source_mask_id``propagation_seed_key` 计算可同步的传播帧;若存在其它传播帧,先弹出范围选择,让用户选择只处理当前帧或处理所有传播帧。处理所有传播帧时,在每个具备对应关系的帧上执行同一次 union只删除该帧参与合并的次级 mask避免把同链但未参与同步的区域整链误删。
7. `area_remove` 使用 difference从第一个选中的主 mask 中扣除后续选中 mask扣除对象本身保留同样会在执行前计算可同步的传播帧并弹出当前帧/所有传播帧选择。处理所有传播帧时,会在其它传播帧中找到对应主区域和扣除区域并执行 difference扣除区域本身继续保留如果 difference 产生内洞,`segmentation` 保留外圈和 hole ring`metadata.polygonRingCounts` 记录每个 polygon 的 ring 数,渲染时使用 even-odd fill。
8. 结果会重算 `pathData``segmentation``bbox``area`,已保存主 mask 会进入 dirty 状态并复用归档 PATCH 链路;同步到传播帧时保留传播来源和 lineage metadata避免自动传播帧在时间轴上变成人工/AI 标注帧;带洞结果的面积按外圈减内洞计算;进入调整多边形时,外圈和内洞 ring 都会显示顶点和边中点插入手柄,内洞拖动、插点、保存与回显继续保持中空结构。
### GT Mask 导入
@@ -270,7 +270,7 @@
- `importGtMask()` 使用 `POST /api/ai/import-gt-mask` multipart form-data并传入 `unknown_color_policy=discard|undefined`。前端上传前弹出导入结果预览和未知 maskid 策略选择;后端使用 `cv2.IMREAD_UNCHANGED` 读取后校验 dtype。合法 GT mask 限定为 8-bit 灰度图或 8-bit RGB 三通道完全相同的 `[X,X,X]` maskid 图0 为背景、X 为 1-255 的 maskid灰度/RGB 等通道图按模板 `maskId` 匹配类别16-bit/uint16 GT_label、全背景 0 图和普通彩色 RGB 类别图不再按颜色匹配并会返回格式错误全背景图提示为“GT Mask 图片中没有非背景 maskid 区域。”;未知类别按策略舍弃或保存为 `gt_unknown_class` 未定义类别。若 GT mask 尺寸和当前帧不同,后端用最近邻插值拉伸到当前帧尺寸后再生成高精度 polygon。
- `getProjectAnnotations()` 使用 `GET /api/ai/annotations`
- `updateAnnotation()` 使用 `PATCH /api/ai/annotations/{annotationId}`
- `deleteAnnotation()` 使用 `DELETE /api/ai/annotations/{annotationId}`
- `deleteAnnotation()` 使用 `DELETE /api/ai/annotations/{annotationId}`;工作区批量删除前会先用 `getProjectAnnotations()` 预检当前项目存在的 id跳过本地陈旧 id避免已被撤销/清空流程删除过的 annotation 再次发起 DELETE 产生 404
- `parseMedia()` 使用 `POST /api/media/parse?project_id=...`,可选 `parse_fps``max_frames``target_width`,用于生成标准帧序列。
- `getProjectFrames()` 返回帧图像 URL、宽高、`timestamp_ms``source_frame_number`
- 后端 `/api/ai/predict` 当前支持 SAM 2.1 的 point、box、interactive`semantic` 文本提示禁用并返回 400。
@@ -297,7 +297,7 @@
以下能力属于当前冻结版本的占位或半可用功能:
- Dashboard 初始快照来自 `GET /api/dashboard/overview`;任务进度区由 `processing_tasks` queued/running/success/failed/cancelled 任务生成,处理中统计只计算 queued/running。
- 已保存标注支持通过右侧语义分类树换标签、polygon 顶点拖动/删除、边中点插入、多 polygon 子区域编辑、中空 mask 内洞 ring 编辑和区域合并/去除进入 dirty 状态并归档更新Canvas 右下角不再提供旧的“应用分类”按钮,避免没选区时误改整帧;区域合并/去除会同步到其它传播帧的对应 mask,并保留传播帧来源 metadata选中整块 mask 可用 Delete/Backspace 删除同步后端,同传播链自动传播结果会随传播 seed/传播结果删除而一并清理,独立 AI 推理/人工 mask 保留。
- 已保存标注支持通过右侧语义分类树换标签、polygon 顶点拖动/删除、边中点插入、多 polygon 子区域编辑、中空 mask 内洞 ring 编辑和区域合并/去除进入 dirty 状态并归档更新;多 polygon/分离区域选中后所有子区域都显示编辑手柄,同帧同传播链的分散 mask 会联动高亮;Canvas 右下角不再提供旧的“应用分类”按钮,避免没选区时误改整帧;区域合并/去除会在存在传播帧时弹窗选择当前帧或所有传播帧,并保留传播帧来源 metadata选中整块 mask 可用 Delete/Backspace 或左侧 `DEL` 删除同步后端前会预检 id,同传播链自动传播结果会随传播 seed/传播结果删除而一并清理,独立 AI 推理/人工 mask 保留。
- SAM 3 文本语义分割已从当前产品路径中禁用相关源码保留恢复时需要重新接入前端入口、registry、状态接口和测试。
- 自定义分类通过 `PATCH /api/templates/{id}` 写入当前激活模板的 `mapping_rules.classes`
- 选中 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。前端不再展示“后端模型置信度”。