news 2026/9/16 18:21:13

Rerun 中 ClassDescriptionMapElem 详解:AnnotationContext 语义类映射的编码原理与实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Rerun 中 ClassDescriptionMapElem 详解:AnnotationContext 语义类映射的编码原理与实战

Rerun 中 ClassDescriptionMapElem 详解:AnnotationContext 语义类映射的编码原理与实战

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

导读

ClassDescriptionMapElem是 Rerun(用于可视化、查询与流式处理多模态机器人数据的开源项目)中一个编码层(encodings)辅助类型,负责把语义类别 ID(ClassId)映射到对应的类别描述(ClassDescription),是AnnotationContext标注上下文组件的内部键值对元素。本文将围绕该类型,从字段语义、Arrow 内存布局、与周边类型(ClassDescription/AnnotationInfo/ClassId)的嵌套关系,到 Python / Rust / C++ 三种 SDK 的实战用法逐层展开,帮助你理解 Rerun 是如何在分割、关键点与骨架连线场景中完成"类别 ID → 名称与颜色"的解析。

类型定位:AnnotationContext 内部的键值对

在 class_description_map_elem.md 中,ClassDescriptionMapElem被明确定义为:

一个用于将encodings.ClassId映射到类别描述的辅助类型,内部使用于components.AnnotationContext

这意味着你通常不会直接以独立组件的形式向数据流中写入它,而是把它作为AnnotationContext的列表元素批量提交。对应的 Rust 组件定义位于 annotation_context.rs,可以看到AnnotationContext的字段就是一个Vec<ClassDescriptionMapElem>

pub struct AnnotationContext( /// List of class descriptions, mapping class indices to class names, colors etc. pub Vec<crate::encodings::ClassDescriptionMapElem>, );

即:一个标注上下文 = 若干条 "类别 ID → 类别描述" 的映射记录,每条记录就是一个ClassDescriptionMapElem。类型层面的约束定义在类型定义源文件 annotation_context.def.rs 中:

pub struct AnnotationContext { pub class_map: Vec<rerun::encodings::ClassDescriptionMapElem>, }

字段语义:键与值

ClassDescriptionMapElem只有两个字段,语义上构成典型的"键值对":

字段类型含义
class_id非空ClassId:一个 16 位语义类别 ID(即components.ClassId
class_description非空ClassDescription:该类别的名称、颜色等全部描述信息

其中class_id在 class_id.md 中被定义为一个16 位(UInt16)的语义类别 ID。它本身只是一个数字标识;真正承载"这个 ID 显示成什么文字、什么颜色"的是class_description这个值。

从 Rust 的自动生成实现 class_description_map_elem.rs 可以看到完全一致的结构:

pub struct ClassDescriptionMapElem { /// The key: the `components::ClassId`. pub class_id: crate::encodings::ClassId, /// The value: class name, color, etc. pub class_description: crate::encodings::ClassDescription, }

Arrow 内存布局:完整嵌套的 Struct

作为 Rerun 数据模型的一等公民,ClassDescriptionMapElem通过 Arrow 格式序列化。其完整布局(来自 class_description_map_elem.md)如下:

Struct( "class_id": non-null UInt16 "class_description": non-null Struct( "info": non-null Struct( "id": non-null UInt16 "label": Utf8 "color": UInt32 ) "keypoint_annotations": non-null List(non-null Struct( "id": non-null UInt16 "label": Utf8 "color": UInt32 )) "keypoint_connections": non-null List(non-null Struct( "keypoint0": non-null UInt16 "keypoint1": non-null UInt16 )) ) )

这个布局可以从实现层面得到验证:Rust 代码中的arrow_data_type()to_arrow_opt会生成一个包含class_idUInt16)和class_description(嵌套Struct)两个非空字段的StructArray,序列化细节参见 class_description_map_elem.rs;反序列化时同样按字段名从StructArray中取出class_idclass_description并强制非空(class_description_map_elem.rs)。

理解这个布局的关键点:

  • 顶层是 Struct:键与值被绑定为一条不可分割的记录;
  • class_id是非空 UInt16:任何映射都必须有明确键,不允许缺失;
  • class_description是非空 Struct:其内部又分为类别信息(info)、关键点标注列表(keypoint_annotations)与关键点连线列表(keypoint_connections)三个子字段;
  • 连线是一对 UInt16keypoint0/keypoint1各指一个关键点 ID。

值对象拆解:ClassDescription 与 AnnotationInfo

要真正用好ClassDescriptionMapElem,需要理解其"值"一侧的两个下层类型。

ClassDescription:一个语义类的完整描述

class_description.md 将ClassDescription定义为"一个语义类的描述":

  • info(非空AnnotationInfo):类别自身的标注信息,负责派生标签与颜色;
  • keypoint_annotations(非空AnnotationInfo列表):该类别下所有关键点的标注信息;
  • keypoint_connections(非空KeypointPair列表):关键点之间的连线(骨架边)。

其工作方式是:当一个实体被ClassId标注后,Rerun 使用该类别对应的AnnotationInfo派生标签与颜色;类内的关键点则用KeypointId标注,并优先使用关键点自身关联的AnnotationInfo的标签与颜色。若某条keypoint_connections中定义了两点间的连线,且这两个关键点都存在于该类别的实例中,则两点之间绘制一条边,边的标签与颜色同样取自该类别的AnnotationInfo

AnnotationInfo:ID、标签与颜色三元组

annotation_info.md 定义了AnnotationInfo的三个字段:

字段类型含义
id非空UInt16该标注所属的ClassIdKeypointId
labelUtf8(可空)在 UI 中显示的标签
colorRgba32UInt32,可空)应用到被标注实体上的颜色

可见ClassDescriptionMapElem的 Arrow 布局其实就是把这套三元组(id/label/color)在infokeypoint_annotations两处复用。

使用场景:AnnotationContext 的查找语义

ClassDescriptionMapElem并不孤立存在,它服务的是 AnnotationContext 组件 的显示语义:

  1. 实体通过ClassIdKeypointId表达"我属于哪个语义类别";
  2. 标签与颜色在适当的标注上下文中查找;
  3. 查找时沿着实体的路径层级向上遍历祖先,取遇到的第一个标注上下文。

也就是说,把AnnotationContext挂在某个实体路径(如masksdetections)上,会作用于该路径下所有子孙实体。该行为在 annotation_context.py 与类型定义源 annotation_context.def.rs 中有明确说明。archetype 层面的 annotation_context.md 还指出其可在 Spatial2DView、Spatial3DView 与 DataframeView 中展示。

三语言实战:如何构造 ClassDescriptionMapElem

下面三个示例(来自 docs/snippets/all/tutorials 的同一教程,三种语言实现相同逻辑)完整演示了从AnnotationInfo/ClassDescription构造映射的两种典型路径。

Python:直接把 AnnotationInfo / ClassDescription 交给 AnnotationContext

在 annotation_context.py 中:

import rerun as rr rr.init("rerun_example_annotation_context_connections") # 两个类别:一个纯标签,一个带颜色 rr.log( "masks", # 作用于 "masks" 之下的所有实体 rr.AnnotationContext( [ rr.AnnotationInfo(id=0, label="Background"), rr.AnnotationInfo(id=1, label="Person", color=(255, 0, 0)), ], ), static=True, ) # 带关键点与骨架连线的类别 rr.log( "detections", # 作用于 "detections" 之下的所有实体 rr.ClassDescription( info=rr.AnnotationInfo(0, label="Snake"), keypoint_annotations=[ rr.AnnotationInfo(id=i, color=(0, 28 * i, 0)) for i in range(10) ], keypoint_connections=[(i, i + 1) for i in range(9)], ), static=True, )

Python 端之所以能这样直接传AnnotationInfoClassDescription,是因为转换器会自动补全ClassDescriptionMapElem:若传入的是AnnotationInfo,则先包装成ClassDescription,再取class_description.info.id作为class_id生成映射元素;具体逻辑见 class_description_map_elem_ext.py。序列化时,转换器把每个元素拆回ClassIdBatchClassDescriptionBatch两个 Arrow 数组,再合并成StructArray(class_description_map_elem_ext.py)。

Rust:显式使用 ClassDescriptionMapElem::from

在 annotation_context.rs 中,映射元素通过元组或结构体字面量构造:

use rerun::{ AnnotationContext, AnnotationInfo, ClassDescription, Rgba32, encodings::{ClassDescriptionMapElem, KeypointId}, }; let rec = rerun::RecordingStreamBuilder::new( "rerun_example_annotation_context_connections", ).spawn()?; // 从 (id, label) 与 (id, label, color) 元组构造映射元素 rec.log_static( "masks", // 作用于 "masks" 之下的所有实体 &AnnotationContext::new([ ClassDescriptionMapElem::from((0, "Background")), ClassDescriptionMapElem::from((1, "Person", Rgba32::from_rgb(255, 0, 0))), ]), )?; // 显式构造 ClassDescription 后包装成映射元素 rec.log_static( "detections", // 作用于 "detections" 之下的所有实体 &AnnotationContext::new([ClassDescription { info: (0, "Snake").into(), keypoint_annotations: (0..10) .map(|i| AnnotationInfo { id: i, label: None, color: Some(Rgba32::from_rgb(0, (28 * i) as u8, 0)), }) .collect(), keypoint_connections: (0..9) .map(|i| (KeypointId(i), KeypointId(i + 1))) .map(Into::into) .collect(), }]), )?;

这些From转换由 SDK 扩展实现提供:(u16, &str)(u16, &str, Rgba32)AnnotationInfoClassDescription都可以直接转换成ClassDescriptionMapElem,其中从ClassDescription转换时自动取info.id作为class_id(见 class_description_map_elem_ext.rs)。

C++:通过容器构造

在 annotation_context.cpp 中:

#include <rerun.hpp> int main(int argc, char* argv[]) { const auto rec = rerun::RecordingStream("rerun_example_annotation_context_connections"); rec.spawn().exit_on_failure(); // 两个类别:一个纯标签,一个带颜色 rec.log_static( "masks", // 作用于 "masks" 之下的所有实体 rerun::AnnotationContext({ rerun::AnnotationInfo(0, "Background"), rerun::AnnotationInfo(1, "Person", rerun::Rgba32(255, 0, 0)), }) ); // 关键点 + 骨架连线 std::vector<rerun::AnnotationInfo> keypoint_annotations; for (uint16_t i = 0; i < 10; ++i) { keypoint_annotations.push_back(rerun::AnnotationInfo( i, rerun::Rgba32(0, static_cast<uint8_t>(28 * i), 0) )); } std::vector<rerun::KeypointPair> keypoint_connections; for (uint16_t i = 0; i < 9; ++i) { keypoint_connections.push_back(rerun::KeypointPair(i, i + 1)); } rec.log_static( "detections", // 作用于 "detections" 之下的所有实体 rerun::AnnotationContext({rerun::ClassDescription( rerun::AnnotationInfo(0, "Snake"), keypoint_annotations, keypoint_connections )}) ); }

注意三处示例都使用了static=True(Python)或log_static(Rust / C++):标注上下文通常是在数据录制开始时一次性定义的静态元数据,无需随时间变化。

从定义到绑定的生成链

ClassDescriptionMapElem这类类型并非手工维护,而是由 Rerun 的类型构建器自动生成。以 Rust 为例,生成的代码头部标注了来源:

// DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/rust/api.rs // Based on "crates/build/re_type_definitions/rerun/encodings/class_description_map_elem.def.rs".

也就是说,类型语义的"唯一事实来源"是crates/build/re_type_definitions下的定义文件,构建器据此生成 Rust、Python、C++ 三种语言绑定(Rust 绑定在 class_description_map_elem.rs 与其_ext扩展文件,Python 绑定在 rerun_py/rerun_sdk/rerun 下的 encodings 与 components 目录)。这也是"编码层类型 + 组件层类型"这一分层设计的由来:ClassDescriptionMapElemClassDescriptionAnnotationInfo归属encodings(纯编码结构),而AnnotationContext归属components(可落盘组件),后者通过Vec<ClassDescriptionMapElem>复用前者。

稳定性与使用注意事项

  • 标记为 unstableClassDescriptionMapElem在文档与源码中均带有⚠️ This type is _unstable_ and may change significantly in a way that the data won't be backwards compatible.警告,且状态标注为#[rerun(state = "unstable")](见 annotation_context.def.rs)。这意味着该类型在后续版本中可能以不向后兼容的方式变化,涉及数据落盘与长期归档时需关注版本对应关系。
  • 不直接独立使用:它是AnnotationContext的内部实现细节。日常开发中优先通过rr.AnnotationContext(...)/AnnotationContext::new(...)高阶 API 构造,转换器会自动补齐class_id
  • 查找语义依赖路径层级:标注上下文沿实体路径向上查找第一个匹配项,因此把上下文挂在越靠近根的位置,影响范围越大;挂得越深,越能覆盖局部子树的显示规则。

总结

ClassDescriptionMapElem是 Rerun 标注体系中最小的"映射单元":一条记录绑定一个 16 位ClassId与其完整的ClassDescription(类别信息 + 关键点标注 + 骨架连线),多个映射元素聚合成AnnotationContext,再通过路径层级查找为分割掩码、检测框和关键点骨架提供标签与颜色。理解它的字段语义、Arrow 嵌套布局与三语言构造方式,即可在机器人视觉数据可视化中高效地定制语义标注显示规则。

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 18:19:56

pyannote.audio 实战入门:10 分钟安装并跑通说话人日志

pyannote.audio 实战入门&#xff1a;10 分钟安装并跑通说话人日志 【免费下载链接】pyannote-audio Neural building blocks for speaker diarization: speech activity detection, speaker change detection, overlapped speech detection, speaker embedding 项目地址: h…

作者头像 李华
网站建设 2026/9/16 18:19:22

星际争霸AI开发入门:BWAPI 4.4.0配置与ualbertabot编译运行全攻略

这几年看AI打《星际争霸》的视频&#xff0c;总觉得隔着层纱。直到我自己动手&#xff0c;把 BWAPI 4.4.0 和 ualbertabot 这套环境跑通&#xff0c;让电脑自己打了一局“人机对战”&#xff0c;才算真正明白那些 Bot 是怎么“想”的。这篇笔记就是我的 BWAI 学习记录第 001 篇…

作者头像 李华
网站建设 2026/9/16 18:18:36

STM32F407老人健康监测系统:心率跌倒检测与嵌入式医疗级设计

简介&#xff1a;本资源是一套面向嵌入式开发初学者与老年健康设备项目实践者的完整智能硬件方案&#xff0c;聚焦于STM32F4平台的老人健康监测智能手表设计&#xff0c;解决跌倒预警、生命体征实时采集与远程监护等实际需求&#xff0c;适用于课程设计、毕业设计及IoT健康终端…

作者头像 李华
网站建设 2026/9/16 18:18:07

RH850定时器中断实战:从TAU配置到GHS链接脚本全解析

简介&#xff1a;面向瑞萨RH850/F1K汽车级32位MCU开发者的定时器中断基础例程&#xff0c;集中演示定时器外设的初始化流程、中断服务程序挂载方式与中断向量表配置方法&#xff0c;覆盖从底层寄存器操作到GHS工程构建的关键环节&#xff0c;适合嵌入式工程师及单片机学习者快速…

作者头像 李华
网站建设 2026/9/16 18:16:20

STM32F103R6数字电压表:ADC双通道采样与数码管动态扫描时序协同设计

简介&#xff1a;本资源是一套基于Proteus与Keil MDK联合仿真的STM32F103R6数字电压表完整工程&#xff0c;面向嵌入式初学者及课程设计实践者&#xff0c;解决两路模拟电压采集、AD转换与数码管实时显示的核心教学需求。压缩包含599个文件&#xff0c;以337个C源码和106个头文…

作者头像 李华
网站建设 2026/9/16 18:16:01

NPUCTF2020 Web题刷题复盘:PHP代码审计与SQL注入过滤绕过实战

NPUCTF2020是前几年打得比较火的一场CTF&#xff0c;Web方向的题目数量不算多&#xff0c;但质量在入门到进阶的区间里卡得很准。我刷这套题的时候正好在补PHP代码审计和手工注入的基础&#xff0c;一轮下来收获最大的不是某一道题的flag&#xff0c;而是把几个高频绕过点串成了…

作者头像 李华