feat: 打通全栈标注闭环、异步拆帧与模型状态

后端能力:

- 新增 Celery app、worker task、ProcessingTask 模型、/api/tasks 查询接口和 media_task_runner,将 /api/media/parse 改为创建后台任务并由 worker 执行 FFmpeg/OpenCV/pydicom 拆帧。

- 新增 Redis 进度事件模块和 FastAPI Redis pub/sub 订阅,将 worker 任务进度广播到 /ws/progress;Dashboard 后端概览接口改为聚合 projects/frames/annotations/templates/processing_tasks。

- 统一项目状态为 pending/parsing/ready/error,新增共享 status 常量,并让前端兼容归一化旧状态值。

- 扩展 AI 后端:新增 SAM registry、SAM2 真实运行状态、SAM3 状态检测与文本语义推理适配入口,以及 /api/ai/models/status GPU/模型状态接口。

- 补齐标注保存/更新/删除、COCO/PNG mask 导出相关后端契约和模板 mapping_rules 打包/解包行为。

前端能力:

- 新增运行时 API/WS 地址推导配置,前端 API 封装对齐 FastAPI 路由、字段映射、任务轮询、标注归档、导出下载和 AI 预测响应转换。

- Dashboard 改为读取 /api/dashboard/overview,并订阅 WebSocket progress/complete/error/status 更新解析队列和实时流转记录。

- 项目库导入视频/DICOM 后创建项目、上传媒体、触发异步解析并刷新真实项目列表。

- 工作区加载真实帧、无帧时触发解析任务、回显已保存标注、保存未归档 mask、更新 dirty mask、清空当前帧后端标注、导出 COCO JSON。

- Canvas 支持当前帧点/框提示调用后端 AI、渲染推理/已保存 mask、应用模板分类并维护保存状态计数;时间轴按项目 fps 播放。

- AI 页面新增 SAM2/SAM3 模型选择,预测请求携带 model;侧边栏和工作区新增真实 GPU/SAM 状态徽标。

- 模板库和本体面板接入真实模板 CRUD、分类编辑、拖拽排序、JSON 导入、默认腹腔镜分类和本地自定义分类选择。

测试与文档:

- 新增 Vitest 配置、前端测试 setup、API/config/websocket/store/组件测试,覆盖登录、项目库、Dashboard、Canvas、工作区、模型状态、时间轴、本体和模板库。

- 新增 pytest 后端测试夹具和 auth/projects/templates/media/AI/export/dashboard/tasks/progress 测试,使用 SQLite、fake MinIO、fake SAM registry 和 Redis monkeypatch 隔离外部服务。

- 新增 doc/ 文档结构,冻结当前需求、设计、接口契约、测试计划、前端逐元素审计、实现地图和后续实施计划,并同步更新 README 与 AGENTS。

验证:

- conda run -n seg_server pytest backend/tests:27 passed。

- npm run test:run:54 passed。

- npm run lint、npm run build、compileall、git diff --check 均通过;Vite 仅提示大 chunk 警告。
This commit is contained in:
2026-05-01 13:29:14 +08:00
parent 4d65c37c73
commit f020ff3b4f
78 changed files with 7089 additions and 456 deletions

View File

@@ -0,0 +1,155 @@
# 当前设计冻结文档
冻结日期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、工具状态 |
| 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 页面 |
| 时间轴 | `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` | SAM 3 状态检测和文本语义推理适配 |
| SAM Registry | `backend/services/sam_registry.py` | 模型选择、GPU 状态和推理分发 |
## 状态模型
前端 store 的核心对象:
- `Project`项目基本信息、状态、帧数、fps、媒体路径。
- `Frame`:帧 ID、项目 ID、索引、图片 URL、宽高。
- `Template` / `TemplateClass`:模板和分类定义。
- `Mask`:前端渲染用 mask包含 `pathData``segmentation``bbox``area`
- `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. `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. `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 并打包 ZIP。
3. 前端“导出 JSON 标注集”按钮会在导出前保存待归档标注,然后下载 COCO JSON。
## 接口契约
接口详情见 `doc/04-api-contracts.md`。测试中重点固定以下契约:
- `updateProject()` 使用 `PATCH /api/projects/{id}`
- `exportCoco()` 使用 `GET /api/export/{projectId}/coco`
- `predictMask()` 使用 `POST /api/ai/predict`,请求体为 `image_id``prompt_type``prompt_data``model`
- `saveAnnotation()` 使用 `POST /api/ai/annotate`
- `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 的真实运行状态。
- 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 任务生成。
- 多边形、矩形、圆、点、线手工绘制未实现。
- 合并、去除、撤销、重做未实现。
- 工作区导出 PNG mask ZIP 按钮尚未提供。
- 已保存标注支持通过“应用分类”进入 dirty 状态并归档更新;暂未提供逐点几何编辑器。
- SAM 3 文本语义分割取决于官方依赖和 GPU 运行环境;状态接口会暴露真实可用性。
- 自定义分类只存在本地组件状态。