diff --git a/components/label/components/PaperContainer.renderTrackingAnnotation.md b/components/label/components/PaperContainer.renderTrackingAnnotation.md new file mode 100644 index 0000000..ef53d7b --- /dev/null +++ b/components/label/components/PaperContainer.renderTrackingAnnotation.md @@ -0,0 +1,96 @@ +# `renderTrackingAnnotation` 流程说明 + +来源文件: + +- `labelimage/components/label/components/PaperContainer.tsx` +- `labelimage/components/label/LabelNossr.tsx` +- `labelimage/components/label/api/label/index.ts` + +## 概述 + +`renderTrackingAnnotation` 是单对象跨帧追踪的入口方法。 + +它会把当前激活图片上选中的标注对象作为 seed,将其轮廓数据发送给追踪模型接口,再把模型返回的后续帧轮廓结果写回 `label store`。 + +## Mermaid + +```mermaid +flowchart TD + A["在 `LabelNossr.tsx` 中按下 `i`"] --> B["调用 `paperContainerRef.current.renderTrackingAnnotation()`"] + B --> C["执行 `updateLoadingFlag(true)`
并显示加载通知"] + C --> D["从 store 读取运行时状态:
`selectedPath[activeImage]`
`selectedItems`
`rasterSize[activeImage]`
`rasterScale[activeImage]`"] + + D --> E{"是否恰好选中了一个标注对象?"} + E -->|未选中| F["提示错误:
`请先选中标注对象后再进行模型调用`"] + E -->|选中了多个| G["提示错误:
`请勿选择多个标注对象进行模型调用`"] + E -->|是| H["在 `label.get(activeImage)` 中查找
满足 `item[0] === id` 的标注对象"] + + H --> I{"是否找到 `categoryId` 和 `data`?"} + I -->|否| Z["跳过请求参数构造"] + I -->|是| J["提取源标注几何数据:
`data[1]` => 外轮廓 `segmentation`
`data[3]` => 镂空轮廓 `hollow_segmentation`"] + + J --> K["将画布坐标转换为原图坐标:
`Math.round(x / currentScale)`
`Math.round(y / currentScale)`"] + K --> L["构造请求参数:
`project_id`
`file_names = [activeImage, ...selectedItems]`
`image_hw = [height, width]`
`contours`
`hollows`"] + L --> M["使用 `msgpack.encode(params)` 序列化"] + M --> N["通过 `getTrackingAuxiliaryAnnotation()`
POST 到 `/api/model_server/sam2/vos`"] + N --> O["以 `arraybuffer` 形式接收响应体"] + O --> P["使用
`msgpack.decode(new Uint8Array(res))`
解码响应数据"] + + P --> Q{"resData.msg === 'success'?"} + Q -->|否| R["内层流程仅执行 `console.log(error/resData)`"] + Q -->|是| S["读取响应中的 `future_contours`"] + + S --> T["克隆当前 `label store`:
`safeClone(useLabelStore.getState().label)`"] + T --> U["遍历每个未来帧结果:
目标文件 = `file_names[index + 1]`"] + U --> V["读取目标帧缩放比例:
`rasterScale[fileName]`"] + V --> W["基于 `pathIds` 生成 `newId` 并注册"] + W --> X["将原图坐标还原为画布坐标:
`x * imgScale`
`y * imgScale`"] + X --> Y["将返回轮廓拆分为:
`segmentation`
`hollow_segmentation`"] + Y --> AA["构造标注元组:
`[newId, segmentation, detail, hollow_segmentation]`"] + AA --> AB["将结果合并到
`Map>`"] + AB --> AC["持久化结果:
`setLabel(nowTaskData)`
`pushStateStack(nowTaskData)`"] + AC --> AD["显示成功通知"] + + F --> AE["finally:执行 `updateLoadingFlag(false)`"] + G --> AE + Z --> AE + R --> AE + AD --> AE + C --> AF["外层 `try/catch`"] + AF -->|异常| AG["显示通用失败通知:
`模型生成失败`"] + AG --> AE +``` + +## 数据结构 + +被追踪的源标注对象,使用和普通多边形标注一致的元组结构: + +```ts +;[id, segmentation, detail, hollow_segmentation] +``` + +各字段含义: + +- `item[0]`:标注 id +- `item[1]`:外轮廓多边形 +- `item[2]`:详情元数据 +- `item[3]`:镂空轮廓多边形 + +## 为什么使用 `arraybuffer` + +这条追踪请求并不是从头到尾都按普通 JSON 方式传输。 + +- 请求参数先通过 `msgpack.encode(params)` 打包。 +- API 封装层把这份打包后的数据发给后端。 +- 响应体以 `arraybuffer` 的形式返回。 +- `renderTrackingAnnotation` 再通过 `msgpack.decode(new Uint8Array(res))` 还原出 JavaScript 对象。 + +因此这里必须配置 `responseType: "arraybuffer"`,因为前端预期拿到的是二进制响应体,再自行解码。 + +## 备注 + +- 当前帧是 seed 帧,模型返回的结果会写入 `file_names[index + 1]` 对应的后续帧。 +- 这个方法更新的是 `label store`,不会直接在当前页面把其他帧的结果立刻画出来。 +- 这个方法只支持一次追踪一个被选中的对象。 +- 内层请求失败时目前主要是 `console.log`,不一定总会显示专门的失败提示。 +- 这个方法里生成的 `detail.image_id` 使用的是 `activeImage`,建议再结合整体数据模型确认一下是否符合预期。