feat: 建立 SAM2 标注闭环基线
- 打通工作区真实标注闭环:支持手工多边形、矩形、圆形、点区域和线段生成 mask,并可保存、回显、更新和删除后端 annotation。 - 增强 polygon 编辑器:支持顶点拖动、顶点删除、边中点插入、多 polygon 子区域选择编辑,以及区域合并和区域去除。 - 接入 GT mask 导入:后端支持二值/多类别 mask 拆分、contour 转 polygon、distance transform seed point,前端支持导入、回显和 seed point 拖动编辑。 - 完善导出能力:COCO JSON 导出对齐前端,PNG mask ZIP 同时包含单标注 mask、按 zIndex 融合的 semantic_frame 和 semantic_classes.json。 - 打通异步任务管理:新增任务取消、重试、失败详情接口与 Dashboard 控件,worker 支持取消状态检查并通过 Redis/WebSocket 推送 cancelled 事件。 - 对接 Dashboard 后端数据:概览统计、解析队列和实时流转记录从 FastAPI 聚合接口与 WebSocket 更新。 - 增强 AI 推理参数:前端发送 crop_to_prompt、auto_filter_background 和 min_score,后端支持点/框 prompt 局部裁剪推理、结果回映射和负向点/低分过滤。 - 接入 SAM3 基础设施:新增独立 Python 3.12 sam3 环境安装脚本、外部 worker helper、后端桥接和真实 Python/CUDA/包/HF checkpoint access 状态检测。 - 保留 SAM3 授权边界:当前官方 facebook/sam3 gated 权重未授权时状态接口会返回不可用,不伪装成可推理。 - 增强前端状态管理:新增 mask undo/redo 历史栈、AI 模型选择状态、保存状态 dirty/draft/saved 流转和项目状态归一化。 - 更新前端 API 封装:补充 annotation CRUD、GT mask import、mask ZIP export、task cancel/retry/detail、AI runtime status 和 prediction options。 - 更新 UI 控件:ToolsPalette、AISegmentation、VideoWorkspace 和 CanvasArea 接入真实操作、导入导出、撤销重做、任务控制和模型状态。 - 新增 polygon-clipping 依赖,用于前端区域 union/difference 几何运算。 - 完善后端 schemas/status/progress:补充 AI 模型外部状态字段、任务 cancelled 状态和进度事件 payload。 - 补充测试覆盖:新增后端任务控制、SAM3 桥接、GT mask、导出融合、AI options 测试;补充前端 Canvas、Dashboard、VideoWorkspace、ToolsPalette、API 和 store 测试。 - 更新 README、AGENTS 和 doc 文档:冻结当前需求/设计/测试计划,标注真实功能、剩余 Mock、SAM3 授权边界和后续实施顺序。
This commit is contained in:
@@ -19,17 +19,17 @@
|
||||
| 模块 | 文件 | 设计职责 |
|
||||
|------|------|----------|
|
||||
| 应用入口 | `src/App.tsx` | 根据登录状态和 `activeModule` 切换页面 |
|
||||
| 全局状态 | `src/store/useStore.ts` | Zustand store,保存项目、帧、模板、mask、工具状态 |
|
||||
| 全局状态 | `src/store/useStore.ts` | Zustand store,保存项目、帧、模板、mask、工具状态和 mask 撤销/重做历史栈 |
|
||||
| API 封装 | `src/lib/api.ts` | Axios 客户端、字段映射、AI 响应转换 |
|
||||
| 配置 | `src/lib/config.ts` | 推导 API 和 WebSocket 地址 |
|
||||
| WebSocket | `src/lib/websocket.ts` | 进度流连接、订阅和重连 |
|
||||
| 模型状态 | `src/components/ModelStatusBadge.tsx` | 展示 GPU 与当前 SAM 模型真实可用状态 |
|
||||
| 登录页 | `src/components/Login.tsx` | 调用登录 API,写入 store |
|
||||
| Dashboard | `src/components/Dashboard.tsx` | 展示统计和 WebSocket 进度消息 |
|
||||
| Dashboard | `src/components/Dashboard.tsx` | 展示统计、任务控制、失败详情和 WebSocket 进度消息 |
|
||||
| 项目库 | `src/components/ProjectLibrary.tsx` | 项目列表、新建、导入视频/DICOM |
|
||||
| 工作区 | `src/components/VideoWorkspace.tsx` | 加载帧和模板,组织工具栏、Canvas、本体面板、时间轴 |
|
||||
| Canvas | `src/components/CanvasArea.tsx` | 显示帧、缩放平移、点/框提示、渲染 mask |
|
||||
| 工具栏 | `src/components/ToolsPalette.tsx` | 切换工具和跳转 AI 页面 |
|
||||
| 工具栏 | `src/components/ToolsPalette.tsx` | 切换工具、跳转 AI 页面、触发 mask 撤销/重做 |
|
||||
| 时间轴 | `src/components/FrameTimeline.tsx` | 帧导航和播放 |
|
||||
| 本体面板 | `src/components/OntologyInspector.tsx` | 模板选择、分类树、本地自定义分类 |
|
||||
| AI 页面 | `src/components/AISegmentation.tsx` | 独立 AI 推理视图,使用当前项目帧 |
|
||||
@@ -51,7 +51,7 @@
|
||||
| AI | `backend/routers/ai.py` | SAM 2 / SAM 3 可选推理、模型状态和标注保存 |
|
||||
| Export | `backend/routers/export.py` | COCO 和 PNG mask 导出 |
|
||||
| SAM 2 | `backend/services/sam2_engine.py` | SAM 2 懒加载、状态检测和点/框/自动推理 |
|
||||
| SAM 3 | `backend/services/sam3_engine.py` | SAM 3 状态检测和文本语义推理适配 |
|
||||
| SAM 3 | `backend/services/sam3_engine.py`, `backend/services/sam3_external_worker.py`, `backend/setup_sam3_env.sh` | SAM 3 状态检测、独立 Python 3.12 环境桥接和文本语义推理适配 |
|
||||
| SAM Registry | `backend/services/sam_registry.py` | 模型选择、GPU 状态和推理分发 |
|
||||
|
||||
## 状态模型
|
||||
@@ -62,6 +62,7 @@
|
||||
- `Frame`:帧 ID、项目 ID、索引、图片 URL、宽高。
|
||||
- `Template` / `TemplateClass`:模板和分类定义。
|
||||
- `Mask`:前端渲染用 mask,包含 `pathData`、`segmentation`、`bbox`、`area`。
|
||||
- `maskHistory` / `maskFuture`:mask 编辑历史栈,用于撤销和重做。
|
||||
- `activeModule`:当前页面。
|
||||
- `activeTool`:当前工具。
|
||||
- `aiModel`:当前选择的 AI 模型,取值为 `sam2` 或 `sam3`。
|
||||
@@ -82,6 +83,14 @@
|
||||
4. Celery worker 执行 FFmpeg/OpenCV/pydicom 拆帧,持续更新 `processing_tasks`,并发布 Redis `seg:progress`。
|
||||
5. 刷新项目列表。
|
||||
|
||||
### 任务控制
|
||||
|
||||
1. Dashboard 从 `GET /api/dashboard/overview` 读取 queued/running/failed/cancelled 任务。
|
||||
2. 用户取消任务时,前端调用 `POST /api/tasks/{task_id}/cancel`;后端写入 `cancelled`、设置 `finished_at`,并尝试 `celery_app.control.revoke(..., terminate=True)`。
|
||||
3. worker 在下载、解析、上传、写帧等关键阶段刷新任务状态;如果发现 `cancelled`,停止后续写入并发布 cancelled 事件。
|
||||
4. 用户重试任务时,前端调用 `POST /api/tasks/{task_id}/retry`;后端基于原任务 `payload` 创建新任务,记录 `retry_of` 并重新投递 Celery。
|
||||
5. 用户打开详情时,前端调用 `GET /api/tasks/{task_id}`,弹窗展示 error、payload、result、Celery ID 和时间。
|
||||
|
||||
### 工作区加载
|
||||
|
||||
1. `VideoWorkspace` 根据 `currentProject.id` 调用 `getProjectFrames()`。
|
||||
@@ -102,6 +111,41 @@
|
||||
9. 工作区加载项目帧后通过 `GET /api/ai/annotations` 取回已保存标注并转成前端 mask。
|
||||
10. 工作区“清空遮罩”删除当前帧已保存标注,并清除当前帧本地 mask。
|
||||
|
||||
### 手工绘制与历史栈
|
||||
|
||||
1. 用户在 `ToolsPalette` 选择多边形、矩形、圆、点或线工具。
|
||||
2. `CanvasArea` 将交互坐标转换成像素 polygon。
|
||||
3. 新 mask 写入 `pathData`、像素 `segmentation`、`bbox`、`area` 和当前模板分类元数据。
|
||||
4. `addMask()`、`setMasks()`、`updateMask()`、`clearMasks()` 会维护 `maskHistory/maskFuture`。
|
||||
5. 工具栏按钮、AI 页按钮和 Canvas Ctrl+Z/Ctrl+Y 调用 `undoMasks()` / `redoMasks()`。
|
||||
|
||||
### Polygon 逐点编辑
|
||||
|
||||
1. 用户点击 Canvas 上的 mask path 后,`CanvasArea` 记录 `selectedMaskId` 并显示该 mask 第一条 polygon 的顶点控制点。
|
||||
2. 拖动顶点后,前端重算 `pathData`、像素 `segmentation`、`bbox`、`area`。
|
||||
3. 如果 mask 已有 `annotationId`,编辑会把 `saveStatus` 标成 `dirty` 且 `saved=false`。
|
||||
4. 归档保存时复用现有 `PATCH /api/ai/annotations/{annotation_id}` 链路,把更新后的 normalized polygon 写回后端。
|
||||
5. 选中顶点后 Delete/Backspace 可删除顶点;前端保持 polygon 至少三点。
|
||||
|
||||
### 区域合并与去除
|
||||
|
||||
1. 用户选择 `area_merge` 或 `area_remove` 后,点击多个当前帧 mask 组成选择集。
|
||||
2. `CanvasArea` 把 `Mask.segmentation` 转为 `polygon-clipping` 的 MultiPolygon。
|
||||
3. `area_merge` 使用 union,更新第一个选中的主 mask,并从前端 store 移除后续被合并 mask;如果被移除 mask 已保存,会调用工作区传入的删除回调删除后端标注。
|
||||
4. `area_remove` 使用 difference,从第一个选中的主 mask 中扣除后续选中 mask,扣除对象本身保留。
|
||||
5. 结果会重算 `pathData`、`segmentation`、`bbox`、`area`,已保存主 mask 会进入 dirty 状态并复用归档 PATCH 链路。
|
||||
|
||||
### GT Mask 导入
|
||||
|
||||
1. 工作区“导入 GT Mask”选择图片文件。
|
||||
2. 前端 `importGtMask()` 以 multipart form-data 调用 `POST /api/ai/import-gt-mask`,携带 `project_id` 和 `frame_id`。
|
||||
3. 后端验证项目、帧、模板后使用 OpenCV 读取灰度 mask。
|
||||
4. 后端按非零像素值拆分多类别标签。
|
||||
5. 后端对每个类别的前景做 contour 提取,每个连通域保存为一个 `Annotation`。
|
||||
6. `points` 字段保存距离变换中心 seed point,`mask_data.polygons` 保存 normalized polygon,`mask_data.gt_label_value` 保存原始像素类别值。
|
||||
7. 前端重新读取项目标注并回显。
|
||||
8. `annotationToMask()` 会把 normalized seed point 转成像素坐标,Canvas 以可拖拽点显示;拖动后 `buildAnnotationPayload()` 会把点再归一化写回后端。
|
||||
|
||||
### 模板管理
|
||||
|
||||
1. `TemplateRegistry` 从后端读取模板。
|
||||
@@ -114,8 +158,10 @@
|
||||
### 导出
|
||||
|
||||
1. 后端根据项目、帧、标注和模板生成 COCO JSON。
|
||||
2. PNG mask 导出会把 normalized polygon 渲染为二值 mask 并打包 ZIP。
|
||||
3. 前端“导出 JSON 标注集”按钮会在导出前保存待归档标注,然后下载 COCO JSON。
|
||||
2. PNG mask 导出会把 normalized polygon 渲染为单标注二值 mask。
|
||||
3. PNG mask 导出还会按 `mask_data.class.zIndex` 或模板 `z_index` 从低到高覆盖,生成每帧语义融合 mask。
|
||||
4. ZIP 内写入 `semantic_classes.json`,记录语义值到类别、颜色和 zIndex 的映射。
|
||||
5. 前端“导出 JSON 标注集”和“导出 PNG Mask ZIP”按钮都会在导出前保存待归档标注,然后下载对应文件。
|
||||
|
||||
## 接口契约
|
||||
|
||||
@@ -123,13 +169,18 @@
|
||||
|
||||
- `updateProject()` 使用 `PATCH /api/projects/{id}`。
|
||||
- `exportCoco()` 使用 `GET /api/export/{projectId}/coco`。
|
||||
- `exportMasks()` 使用 `GET /api/export/{projectId}/masks`。
|
||||
- `cancelTask()` 使用 `POST /api/tasks/{taskId}/cancel`。
|
||||
- `retryTask()` 使用 `POST /api/tasks/{taskId}/retry`。
|
||||
- `predictMask()` 使用 `POST /api/ai/predict`,请求体为 `image_id`、`prompt_type`、`prompt_data`、`model`。
|
||||
- `saveAnnotation()` 使用 `POST /api/ai/annotate`。
|
||||
- `importGtMask()` 使用 `POST /api/ai/import-gt-mask` multipart form-data。
|
||||
- `getProjectAnnotations()` 使用 `GET /api/ai/annotations`。
|
||||
- `updateAnnotation()` 使用 `PATCH /api/ai/annotations/{annotationId}`。
|
||||
- `deleteAnnotation()` 使用 `DELETE /api/ai/annotations/{annotationId}`。
|
||||
- 后端 `/api/ai/predict` 支持 point、box、semantic 三种 prompt_type,并通过 `model` 选择 SAM 2 或 SAM 3。
|
||||
- 后端 `/api/ai/models/status` 返回 GPU、SAM 2、SAM 3 的真实运行状态。
|
||||
- 后端 `/api/ai/predict` 支持可选 `options`:`crop_to_prompt` 会对 point/box prompt 做局部裁剪推理并回映射 polygon,`auto_filter_background` 会按 `min_score` 和负向点过滤结果。
|
||||
- 后端 `/api/ai/models/status` 返回 GPU、SAM 2、SAM 3 的真实运行状态;SAM 3 状态包含外部 Python 环境与 checkpoint access 的可用性。
|
||||
- point prompt 支持旧数组形式和 `{ points, labels }` 对象形式。
|
||||
|
||||
## 外部依赖边界
|
||||
@@ -146,10 +197,8 @@
|
||||
|
||||
以下能力属于当前冻结版本的占位或半可用功能:
|
||||
|
||||
- Dashboard 初始快照来自 `GET /api/dashboard/overview`;解析队列由 `processing_tasks` queued/running 任务生成。
|
||||
- 多边形、矩形、圆、点、线手工绘制未实现。
|
||||
- 合并、去除、撤销、重做未实现。
|
||||
- 工作区导出 PNG mask ZIP 按钮尚未提供。
|
||||
- 已保存标注支持通过“应用分类”进入 dirty 状态并归档更新;暂未提供逐点几何编辑器。
|
||||
- SAM 3 文本语义分割取决于官方依赖和 GPU 运行环境;状态接口会暴露真实可用性。
|
||||
- Dashboard 初始快照来自 `GET /api/dashboard/overview`;解析队列由 `processing_tasks` queued/running/failed/cancelled 任务生成。
|
||||
- 已保存标注支持通过“应用分类”、polygon 顶点拖动/删除、边中点插入、多 polygon 子区域编辑和区域合并/去除进入 dirty 状态并归档更新;复杂洞结构编辑尚未实现。
|
||||
- SAM 3 文本语义分割取决于官方依赖、GPU 运行环境和 Hugging Face gated 权重授权;状态接口会暴露真实可用性,未授权时 `available=false`。
|
||||
- 自定义分类只存在本地组件状态。
|
||||
- GT mask 导入已完成多类别像素值拆分、contour、distance transform seed point 和前端 seed point 拖拽编辑;骨架提取、HDBSCAN 聚类和模板自动映射尚未实现。
|
||||
|
||||
Reference in New Issue
Block a user