Files
Pre_Seg_Server/doc/08-current-design-freeze.md
admin 689a9ba283 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 授权边界和后续实施顺序。
2026-05-01 15:26:25 +08:00

205 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 当前设计冻结文档
冻结日期2026-05-01
本文档描述当前代码结构、数据流、接口契约和测试边界。后续实现如果改变这些设计,应同步更新本文档和测试。
## 总体架构
当前系统由三层组成:
- React + TypeScript 前端 SPA。
- FastAPI 后端 API。
- PostgreSQL、MinIO、Redis、SAM 2 / SAM 3 等外部基础设施。
开发时前端通过 `server.ts` 启动 Express + Vite middleware后端通过 `backend/main.py` 启动 FastAPI。前端业务接口主要访问 FastAPI不依赖 `server.ts` 中保留的旧 mock API。
## 前端模块
| 模块 | 文件 | 设计职责 |
|------|------|----------|
| 应用入口 | `src/App.tsx` | 根据登录状态和 `activeModule` 切换页面 |
| 全局状态 | `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 进度消息 |
| 项目库 | `src/components/ProjectLibrary.tsx` | 项目列表、新建、导入视频/DICOM |
| 工作区 | `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` | 模板选择、分类树、本地自定义分类 |
| AI 页面 | `src/components/AISegmentation.tsx` | 独立 AI 推理视图,使用当前项目帧 |
| 模板库 | `src/components/TemplateRegistry.tsx` | 模板 CRUD、分类编辑、导入、排序 |
## 后端模块
| 模块 | 文件 | 设计职责 |
|------|------|----------|
| 应用入口 | `backend/main.py` | FastAPI app、CORS、路由注册、健康检查、WebSocket |
| 配置 | `backend/config.py` | Pydantic settings |
| 数据库 | `backend/database.py` | SQLAlchemy engine、session、Base |
| 模型 | `backend/models.py` | Project、Frame、Template、Annotation、Mask、ProcessingTask |
| Schema | `backend/schemas.py` | Pydantic 请求/响应模型 |
| Auth | `backend/routers/auth.py` | 开发登录 |
| Projects | `backend/routers/projects.py` | 项目与帧 CRUD |
| Templates | `backend/routers/templates.py` | 模板 CRUD 和 mapping_rules 打包/解包 |
| Media | `backend/routers/media.py` | 上传媒体和拆帧 |
| 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`, `backend/services/sam3_external_worker.py`, `backend/setup_sam3_env.sh` | SAM 3 状态检测、独立 Python 3.12 环境桥接和文本语义推理适配 |
| SAM Registry | `backend/services/sam_registry.py` | 模型选择、GPU 状态和推理分发 |
## 状态模型
前端 store 的核心对象:
- `Project`项目基本信息、状态、帧数、fps、媒体路径。
- `Frame`:帧 ID、项目 ID、索引、图片 URL、宽高。
- `Template` / `TemplateClass`:模板和分类定义。
- `Mask`:前端渲染用 mask包含 `pathData``segmentation``bbox``area`
- `maskHistory` / `maskFuture`mask 编辑历史栈,用于撤销和重做。
- `activeModule`:当前页面。
- `activeTool`:当前工具。
- `aiModel`:当前选择的 AI 模型,取值为 `sam2``sam3`
## 关键数据流
### 登录
1. `Login` 收集用户名和密码。
2. `login()` 调用 `POST /api/auth/login`
3. 成功后 store 写入 tokenApp 渲染主界面。
### 项目导入
1. `ProjectLibrary` 创建项目。
2. 上传视频或 DICOM 到 `/api/media/upload``/api/media/upload/dicom`
3. 调用 `/api/media/parse` 创建异步拆帧任务。
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()`
2. 若无帧但项目有 `video_path`,触发 `parseMedia()`,通过 `getTask()` 轮询任务完成后重新取帧。
3. 帧数据映射为 store `Frame[]`
4. 当前帧传入 `CanvasArea`
### AI 点/框推理
1. 用户在 Canvas 选择正向点、反向点或框选。
2. `CanvasArea` 读取当前帧 ID 和宽高。
3. `predictMask()` 归一化坐标并携带当前 `model` 调用 `/api/ai/predict`
4. 后端加载帧图片并通过 SAM registry 分发到 SAM 2 或 SAM 3。
5. 前端把 `polygons` 转为 mask写入 store。
6. Canvas 按当前帧过滤并渲染 mask。
7. 新 mask 会带上当前选择的模板分类元数据,包括 `classId``className``classZIndex` 和保存状态 `draft`
8. 用户点击“结构化归档保存”后,前端将像素 `segmentation` 转成 normalized `mask_data.polygons`;未保存 mask 调用 `POST /api/ai/annotate`dirty mask 调用 `PATCH /api/ai/annotations/{annotation_id}`
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` 从后端读取模板。
2. 编辑态在组件本地维护分类列表。
3. 保存时调用 `createTemplate()``updateTemplate()`
4. 后端把 `classes``rules` 打包进 `mapping_rules`
5. 返回时再解包给前端。
6. `OntologyInspector` 可以选择具体分类;选择结果进入全局 store`CanvasArea``AISegmentation` 新建/更新 mask 时使用。
### 导出
1. 后端根据项目、帧、标注和模板生成 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”按钮都会在导出前保存待归档标注然后下载对应文件。
## 接口契约
接口详情见 `doc/04-api-contracts.md`。测试中重点固定以下契约:
- `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/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 }` 对象形式。
## 外部依赖边界
测试不直接依赖以下真实服务:
- PostgreSQL后端测试使用内存 SQLite。
- MinIO上传、下载、预签名 URL 使用 monkeypatch。
- Redis单测使用 monkeypatch 验证进度事件发布,不依赖真实 Redis 服务。
- SAMAI 推理测试使用 fake registry。
- 浏览器 Canvas/Konva 图片加载:前端测试 mock `react-konva``use-image`
## 已知占位设计
以下能力属于当前冻结版本的占位或半可用功能:
- 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 聚类和模板自动映射尚未实现。