引入实例ID驱动传播链匹配

- 前端保存标注写入并保留 instance_id,AI 自动推理 seed 携带 source_instance_id,避免同类多 mask 只按语义混在一起。

- 后端传播任务优先用 source_instance_id/instance_id 做幂等、替换和写入前清理,并保留 source_annotation_id/source_mask_id/legacy 兼容路径。

- 前端传播链匹配、删除/分类同步和布尔合并/去重加入实例 token,保持旧 lineage 和空间最近 legacy fallback。

- 补充前后端回归测试,覆盖同类别多实例传播、重传、布尔同步、断开多区域和保存/回显 metadata。

- 更新 AGENTS 与 doc 事实文档,明确 maskid 仍只用于语义分类、GT_label 和导出,不参与实例追踪。
This commit is contained in:
2026-05-04 05:54:23 +08:00
parent 1ff757e2fa
commit 5d73eacefe
15 changed files with 325 additions and 19 deletions

View File

@@ -259,7 +259,8 @@ SAM 2 点提示和 auto fallback 当前只采用最高分候选 mask避免同
"label": "胆囊",
"color": "#ff0000",
"class_metadata": {"id": "c1", "name": "胆囊", "color": "#ff0000", "zIndex": 20, "maskId": 1},
"template_id": 2
"template_id": 2,
"source_instance_id": "instance-123"
}
}
```
@@ -280,7 +281,8 @@ SAM 2 点提示和 auto fallback 当前只采用最高分候选 mask避免同
"seed": {
"polygons": [[[0.1, 0.1], [0.3, 0.1], [0.3, 0.3]]],
"label": "胆囊",
"color": "#ff0000"
"color": "#ff0000",
"source_instance_id": "instance-123"
}
}
]
@@ -288,7 +290,7 @@ 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`,用于在视频处理进度条上把自动传播帧显示为蓝色区段。
后台任务入队接口会先规范化/校验 `model` 字段中的 SAM 2.1 权重 id再把规范化后的权重 id 写入 `processing_tasks.payload.model`;前端提交传播前会先保存当前项目中的 draft/dirty mask使 seed 尽量携带稳定的 `source_annotation_id`,同时仍会携带 `source_mask_id`。如果参考 mask 本身来自自动传播且未被编辑,前端会继承其 `propagation_seed_signature`,让后端识别它仍是原始 seed 的同一条传播链;如果该 mask 被编辑,保存时只保留 `source_annotation_id/source_mask_id` lineage不继承旧签名从而触发旧结果清理和重传。worker 保存传播结果时会写入 `propagation_seed_key``propagation_seed_signature``propagation_direction`。同一目标帧段内,同一 seed、同一权重、同一方向再次传播时如果所有目标帧已有同签名结果worker 会跳过该 seed如果签名变化、目标帧段只部分覆盖或本次改用其他 SAM 2.1 权重worker 会先删除本次目标帧段内的旧自动传播标注再保存新结果。同一参考帧多个同类别 seed 会按 `source_annotation_id``source_mask_id``propagation_seed_key` 区分实例,避免 label/color/class 相同的不同 mask 互相清理;旧版本缺少稳定来源 id 的传播结果才走 label/color/class 兼容清理,避免保存后的 `source_annotation_id` 无法替换旧结果。任务运行中/完成后会写入 `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 进度流显示该任务。
后台任务入队接口会先规范化/校验 `model` 字段中的 SAM 2.1 权重 id再把规范化后的权重 id 写入 `processing_tasks.payload.model`;前端提交传播前会先保存当前项目中的 draft/dirty mask使 seed 尽量携带稳定的 `source_instance_id``source_annotation_id` `source_mask_id`。如果参考 mask 本身来自自动传播且未被编辑,前端会继承其 `source_instance_id``propagation_seed_signature`,让后端识别它仍是原始 seed 的同一条传播链;如果该 mask 被编辑,保存时只保留 lineage不继承旧签名从而触发旧结果清理和重传。worker 保存传播结果时会写入 `instance_id``source_instance_id``propagation_seed_key``propagation_seed_signature``propagation_direction`。同一目标帧段内,同一 seed、同一权重、同一方向再次传播时如果所有目标帧已有同签名结果worker 会跳过该 seed如果签名变化、目标帧段只部分覆盖或本次改用其他 SAM 2.1 权重worker 会先删除本次目标帧段内的旧自动传播标注再保存新结果。同一参考帧多个同类别 seed 会优先`source_instance_id/instance_id` 区分实例,再兼容 `source_annotation_id``source_mask_id``propagation_seed_key`,避免 label/color/class/maskid 相同的不同 mask 互相清理;旧版本缺少稳定来源 id 的传播结果才走 label/color/class 兼容清理,避免保存后的稳定 id 无法替换旧结果。任务运行中/完成后会写入 `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 进度流显示该任务。
## 已完成的接口对齐
@@ -304,7 +306,7 @@ SAM 2.1 变体使用对应 video predictor 的 mask seed 传播;`model=sam2`
- `importGtMask()` 已接入 `POST /api/ai/import-gt-mask`,导入后端生成的高精度 polygon 标注、原始 `gt_label_value`、原图尺寸/是否拉伸信息。导入端使用 `cv2.IMREAD_UNCHANGED` 读取后校验 dtype仅接受 8-bit 灰度图和 8-bit RGB 三通道相等图,并按模板 `maskId` 匹配类别16-bit/uint16 GT_label、全背景 0 图和普通彩色 RGB 类别图都会返回格式错误全背景图保留“GT Mask 图片中没有非背景 maskid 区域。”提示;超出现有类别时由 `unknown_color_policy` 决定舍弃或写为 `gt_unknown_class` 未定义类别。导入 mask 与普通 mask 共用拓扑统计、边缘平滑和保存更新接口,中空导入结果通过 `mask_data.holes``metadata.polygonRingCounts` 回显为可编辑内洞,前端不显示黄色 seed point。
- `exportMasks()` 已接入 `GET /api/export/{projectId}/masks`
- `parseMedia()` 已改为创建 Celery 后台任务,并返回 `ProcessingTask`
- `queuePropagationTask()` 已接入 `/api/ai/propagate/task`,自动传播不再依赖长时间同步 HTTP 请求;传播 seed 可携带与 `polygons` 对齐的 `holes`,后端 seed 签名、SAM 2 seed mask 栅格化和传播结果保存都会保留内洞。
- `queuePropagationTask()` 已接入 `/api/ai/propagate/task`,自动传播不再依赖长时间同步 HTTP 请求;传播 seed 可携带与 `polygons` 对齐的 `holes``source_instance_id`,后端 seed 签名、SAM 2 seed mask 栅格化和传播结果保存都会保留内洞,并用实例 id 区分同语义多 mask
- `getTask()` 已接入 `GET /api/tasks/{taskId}`
- `cancelTask()` 已接入 `POST /api/tasks/{taskId}/cancel`
- `retryTask()` 已接入 `POST /api/tasks/{taskId}/retry`