Files
labelimage/components/label/components/PaperContainer.renderTrackingAnnotation.md
2026-03-17 18:17:08 +08:00

4.5 KiB
Raw Blame History

renderTrackingAnnotation 流程说明

来源文件:

  • labelimage/components/label/components/PaperContainer.tsx
  • labelimage/components/label/LabelNossr.tsx
  • labelimage/components/label/api/label/index.ts

概述

renderTrackingAnnotation 是单对象跨帧追踪的入口方法。

它会把当前激活图片上选中的标注对象作为 seed将其轮廓数据发送给追踪模型接口再把模型返回的后续帧轮廓结果写回 label store

Mermaid

flowchart TD
    A["在 `LabelNossr.tsx` 中按下 `i`"] --> B["调用 `paperContainerRef.current.renderTrackingAnnotation()`"]
    B --> C["执行 `updateLoadingFlag(true)`<br/>并显示加载通知"]
    C --> D["从 store 读取运行时状态:<br/>`selectedPath[activeImage]`<br/>`selectedItems`<br/>`rasterSize[activeImage]`<br/>`rasterScale[activeImage]`"]

    D --> E{"是否恰好选中了一个标注对象?"}
    E -->|未选中| F["提示错误:<br/>`请先选中标注对象后再进行模型调用`"]
    E -->|选中了多个| G["提示错误:<br/>`请勿选择多个标注对象进行模型调用`"]
    E -->|是| H["在 `label.get(activeImage)` 中查找<br/>满足 `item[0] === id` 的标注对象"]

    H --> I{"是否找到 `categoryId` 和 `data`"}
    I -->|否| Z["跳过请求参数构造"]
    I -->|是| J["提取源标注几何数据:<br/>`data[1]` => 外轮廓 `segmentation`<br/>`data[3]` => 镂空轮廓 `hollow_segmentation`"]

    J --> K["将画布坐标转换为原图坐标:<br/>`Math.round(x / currentScale)`<br/>`Math.round(y / currentScale)`"]
    K --> L["构造请求参数:<br/>`project_id`<br/>`file_names = [activeImage, ...selectedItems]`<br/>`image_hw = [height, width]`<br/>`contours`<br/>`hollows`"]
    L --> M["使用 `msgpack.encode(params)` 序列化"]
    M --> N["通过 `getTrackingAuxiliaryAnnotation()`<br/>POST 到 `/api/model_server/sam2/vos`"]
    N --> O["以 `arraybuffer` 形式接收响应体"]
    O --> P["使用<br/>`msgpack.decode(new Uint8Array(res))`<br/>解码响应数据"]

    P --> Q{"resData.msg === 'success'"}
    Q -->|否| R["内层流程仅执行 `console.log(error/resData)`"]
    Q -->|是| S["读取响应中的 `future_contours`"]

    S --> T["克隆当前 `label store`<br/>`safeClone(useLabelStore.getState().label)`"]
    T --> U["遍历每个未来帧结果:<br/>目标文件 = `file_names[index + 1]`"]
    U --> V["读取目标帧缩放比例:<br/>`rasterScale[fileName]`"]
    V --> W["基于 `pathIds` 生成 `newId` 并注册"]
    W --> X["将原图坐标还原为画布坐标:<br/>`x * imgScale`<br/>`y * imgScale`"]
    X --> Y["将返回轮廓拆分为:<br/>`segmentation`<br/>`hollow_segmentation`"]
    Y --> AA["构造标注元组:<br/>`[newId, segmentation, detail, hollow_segmentation]`"]
    AA --> AB["将结果合并到<br/>`Map<fileName, Map<categoryId, annotation[]>>`"]
    AB --> AC["持久化结果:<br/>`setLabel(nowTaskData)`<br/>`pushStateStack(nowTaskData)`"]
    AC --> AD["显示成功通知"]

    F --> AE["finally执行 `updateLoadingFlag(false)`"]
    G --> AE
    Z --> AE
    R --> AE
    AD --> AE
    C --> AF["外层 `try/catch`"]
    AF -->|异常| AG["显示通用失败通知:<br/>`模型生成失败`"]
    AG --> AE

数据结构

被追踪的源标注对象,使用和普通多边形标注一致的元组结构:

;[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,建议再结合整体数据模型确认一下是否符合预期。