Rerun Tensor View 深度解析:任意维度张量的 2D 切片可视化实现与使用指南
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
导读
re_view_tensor是 Rerun(一个面向多模态机器人数据可视化、查询与流式传输的开源工具)家族中的一个专用 View(视图)crate,其核心职责是将任意维度(N-D)的张量以 2D 切片 + 自定义色彩映射(colormap)的方式渲染出来。本文将结合该 crate 的源码、参考文档与测试用例,深入讲解 Tensor View 的架构与数据流、维度映射(slice selection)的启发式规则、标量到颜色的映射参数,以及如何通过 Blueprint 编程式地创建和使用 Tensor View,帮助你直接在 Rerun 中可视化神经网络特征图、深度图、体积数据切片等高维张量数据。
一、模块定位:一个专为"任意维度张量"设计的视图
crates/viewer/re_view_tensor/README.md 给出了这个 crate 最精炼的定义:
A View dedicated to visualizing tensors with arbitrary dimensionality.
即:一个专门用于可视化任意维度张量的视图。它是 Rerun 众多视图(View)之一,对应 Rerun 官方类型体系中的TensorView(见 docs/content/reference/types/views/tensor_view.md),官方文档同时注明该类型当前仍处于unstable阶段,后续可能发生不向后兼容的变化。
在 Rerun 中,"View" 是蓝图(Blueprint)层面的概念:一个 View 决定了一块视口(viewport)区域如何解释并渲染某个 entity(实体)的数据。从源码结构看(crates/viewer/re_view_tensor/src/lib.rs),该 crate 对外只暴露一个核心类型TensorView,内部由五个模块协作完成渲染:
| 模块 | 职责 |
|---|---|
view_class.rs | TensorView视图类的整体实现:布局、帮助信息、选区面板、渲染入口 |
visualizer_system.rs | TensorSystem可视化器:负责从数据存储(Chunk Store)中查询张量数据 |
dimension_mapping.rs | TensorSliceSelection:切片选择(宽/高/索引/滑杆)的加载与合法性修正 |
tensor_dimension_mapper.rs | 维度映射的拖拽式 UI(Dimension Mapping 面板) |
tensor_slice_to_gpu.rs | 将 2D 切片上传为 GPU 纹理并做色彩映射 |
TensorView的显示名为"Tensor"(见 view_class.rs),视图的帮助文本对其功能做了最直接的概括:
An N-dimensional tensor displayed as a 2D slice with a custom colormap. Set the displayed dimensions in a selection panel.
任意 N 维张量 → 选取其中两维作为宽/高 → 其余维度固定索引 → 得到一个 2D 切片 → 用自定义 colormap 渲染。这就是 Tensor View 的全部核心逻辑,下面各节将逐一展开其实现细节。
二、核心数据流:从 Chunk Store 到 GPU 纹理的完整链路
从源码结构看,一次 Tensor View 的渲染大致经过以下四步:
查询张量:
TensorSystem(visualizer_system.rs)作为可视化器(Visualizer),通过VisualizerQueryInfo::single_required_component::<TensorData>声明自己依赖TensorData这一必需组件(关联到Tensorarchetype)。执行时使用LatestAtQuery查询当前时间点上每个数据结果的张量数据,并顺便读取可选的ValueRange组件作为数值范围;若未提供,则使用typed_fallback_for提供的回退值。组装可视结果:查询结果被包装为
TensorVisualization(包含tensor_row_id、tensor: TensorData、data_range: ValueRange三个字段),供视图类使用。切片选择与合法性修正:
TensorView从当前 View 的 Blueprint 属性中读取TensorSliceSelection,通过load_and_make_valid将其修正为对当前张量形状合法有效的切片配置。GPU 上传与渲染:
tensor_slice_to_gpu.rs中colormapped_texture先按切片配置取出 2D 子视图,上传为GpuTexture2D,再与 colormap、gamma 等参数组装成ColormappedTexture,最终由gpu_bridge::render_image绘制到视图画布上(view_class.rs)。
值得注意的实现细节是 GPU 纹理的缓存策略:upload_texture_slice_to_gpu以(tensor_row_id, slice_selection)的哈希作为纹理缓存 key(tensor_slice_to_gpu.rs),这意味着只要张量数据行或切片配置不变,纹理就不会重复上传,拖动滑杆浏览各切片时开销很低。
2.1 数据类型与 GPU 纹理格式映射
张量数据最终必须变成 GPU 纹理,因此TensorData的各种底层类型在 tensor_slice_to_gpu.rs 中被一一映射为 wgpu 纹理格式:
| 张量数据类型 | 纹理格式 | 说明 |
|---|---|---|
U8 | R8Uint | 原样拷贝 |
U16 | R16Uint | 原样拷贝 |
U32 | R32Uint | 原样拷贝 |
U64 | R32Float | 收窄为 f32 |
I8/I16/I32 | R8Sint/R16Sint/R32Sint | 原样拷贝 |
I64 | R32Float | 收窄为 f32 |
F16 | R16Float | 原样拷贝 |
F32 | R32Float | 原样拷贝 |
F64 | R32Float | 收窄为 f32 |
因此从实现事实看,U64、I64、F64这三种高精度类型在 GPU 渲染阶段会被收窄为 f32(可能带来精度损失),而其余类型保持原样。这也是为什么 Tensor View 帮助文本强调色彩映射是"scalar 到 color"的转换——纹理本质上承载的是标量强度值。
三、维度映射(Slice Selection):如何把 N 维张量摊成 2D 图片
这是 Tensor View 最核心的交互与配置概念,对应 Blueprint 属性slice_selection。参考文档 tensor_view.md 将其拆解为四部分:
width:映射到宽度的维度;height:映射到高度的维度;indices:其余所有维度上选取的固定索引;slider:出现在这个列表里的维度会获得一个滑杆(用于交互式切换该维度的索引)。
在源码中,这四个概念被序列化为TensorSliceSelection结构体(dimension_mapping.rs):
pub struct TensorSliceSelection { pub width: Option<TensorWidthDimension>, pub height: Option<TensorHeightDimension>, pub indices: Vec<TensorDimensionIndexSelection>, pub slider: Option<Vec<TensorDimensionIndexSlider>>, }3.1 默认启发式:自动挑选宽高维度
用户不手动指定时,make_width_height_valid与find_width_height_dim_indices(dimension_mapping.rs)负责"猜"出合理的宽高。其规则按优先级排列:
- 按维度名匹配:维度名(小写)属于
w / width / right / left则视为宽度候选,属于h / height / up / down则视为高度候选;其中名为left的维度会自动设置invert(翻转),名为up的维度也会自动设置invert,以便图像方向符合直觉。 - 按长度兜底:取张量中最长与次长的两个维度作为宽高。
- NumPy 约定:当最长的两个维度恰好是第 0、1 维(即二者长度并列第一)时,采用 NumPy 的
[h, w, ...]排布约定——即第 0 维作为高度、第 1 维作为宽度。这一点在单元测试中有明确断言(assert_eq!(wh(&[dim(800), dim(50)]), (1, 0), "numpy ordering"),见 dimension_mapping.rs)。
3.2 合法性修正(make valid):越界即修正,绝不崩溃
load_and_make_valid是"防御式"设计的集中体现,它把 Blueprint 中存储的切片配置就地修正为一个对当前张量形状合法且自洽的配置:
- 越界维度收敛:
width/height的维度下标超过张量维数时,被收敛到最大合法维(dimension_mapping.rs); - 宽高不能同维:若 width 与 height 指向同一维度,则移除 height;
- 维数不足时补齐:二维以上张量强制同时设置 width 与 height;一维张量至少保证 width 或 height 之一被设置;
- 索引越界收敛:
indices中与 width/height 相同维度或越界的条目被移除,其余索引被钳制到size - 1以内; - 缺失索引补中间:未被 width/height/indices 覆盖的维度,自动补充"取中间索引"(
size / 2)的条目(dimension_mapping.rs); - 滑杆自动生成:若未指定
slider,则为每个未被宽高覆盖的维度默认生成一个滑杆。
这些规则都配有单元测试(dimension_mapping.rs),例如测试验证了空配置能产出好的默认值、越界索引被钳制(1000 → 99/199/299)、宽高覆盖的维度从 indices 中被剔除等行为。
3.3 交互方式:拖拽重映射 + 滑杆浏览
在视图的 Selection(选区)面板中,"Dimension Mapping" 区域(view_class.rs)通过dimension_mapping_ui(tensor_dimension_mapper.rs)提供可视化编辑:
- 拖拽:张量的每个维度被渲染为一个可拖拽的标签,可拖到
Image → width、Image → height槽位,或拖入/拖出Selectors列表,从而把任意维度重新映射为宽、高或索引选择器; - Flip(翻转):width/height 各有翻转开关,翻转后轴方向反转;
- 滑杆开关:每个 selector 右侧有一个眼睛按钮,控制该维度是否显示滑杆;
- Reset 按钮:"Reset to default blueprint" 恢复默认蓝图配置,"Reset to heuristic" 则把自定义映射清空、回到启发式默认值(view_class.rs)。
需要说明的是,从源码结构看NewSelector(把维度"暂存"到一个新槽位)功能当前被注释禁用(tensor_dimension_mapper.rs),代码注释表示它暂时没有价值、未来可能需要时再启用。
3.4 切片在代码里如何发生
一旦确定了TensorSliceSelection,实际的切片操作由selected_tensor_slice完成(view_class.rs):
- 一维张量会被 reshape 成
[len, 1]的二维形状,以统一处理; - 将
[height, width] + indices组合为新的轴顺序并做permuted_axes重排; - 对每个索引选择,沿第 2 轴(
Axis(2))固定对应索引(逐层降维); - 根据
invert标志对高度轴(Axis 0)/宽度轴(Axis 1)做翻转。
四、标量映射(Scalar Mapping):从数值到颜色的视觉编码
参考文档将scalar_mapping拆为三个参数:
mag_filter:放大查看张量时使用的纹理过滤方式;colormap:标量值到颜色的映射方案;gamma:颜色映射前对归一化数值施加的gamma 指数。
在paint_tensor_slice(view_class.rs)中,这三个参数从 Blueprint 属性读取,并组装为ColormapWithRange(colormap +[data_range.start, data_range.end]数值范围)传给 GPU 渲染。值得注意的实现细节:
- colormap 的默认值是 Viridis:
on_register中通过 fallback provider 注册,未指定 colormap 时默认使用Colormap::Viridis(view_class.rs); - mag_filter 的取值:
Nearest映射为纹理的Nearest过滤,Linear与Bicubic都被映射为Linear过滤(minification 固定为Linear,mipmap 当前未启用,见 view_class.rs); - 纹理采样:wrap mode 为
ClampToEdge,颜色解码为 opaque、非 sRGB。
五、视图适配(View Fit):切片如何填入视图区域
view_fit属性决定 2D 切片在视图中的摆放方式,源码(view_class.rs)支持三种模式:
view_fit取值 | 行为 |
|---|---|
Original | 按切片原始像素尺寸 1:1 显示 |
Fill | 拉伸填满整个可用区域(忽略宽高比) |
FillKeepAspectRatio | 等比缩放填满(取宽高缩放比的较小者) |
三种模式都需要从TensorViewFitblueprint 属性或回退值中读取,Original模式还做了最小 1×1 的尺寸保护(Vec2::max(Vec2::splat(1.0), img_size)),防止空张量导致的除零等问题。
六、实战:用 Blueprint 编程式创建 Tensor View
Rerun 的官方示例 docs/snippets/all/views/tensor.py 给出了完整的端到端用法——先记录一个四维张量,再用 Blueprint 显式配置 TensorView 的切片、标量映射与视图适配:
import numpy as np import rerun as rr import rerun.blueprint as rrb rr.init("rerun_example_tensor", spawn=True) # 记录一个 4 维张量:(batch, x, y, channel),并给维度起名 tensor = np.random.randint(0, 256, (32, 240, 320, 3), dtype=np.uint8) rr.log("tensor", rr.Tensor(tensor, dim_names=("batch", "x", "y", "channel"))) blueprint = rrb.Blueprint( rrb.TensorView( origin="tensor", name="Tensor", # 显式挑选要显示的维度 slice_selection=rrb.TensorSliceSelection( # 用第 1 个维度作为宽度 width=1, # 用第 2 个维度作为高度,并翻转 height=rr.TensorDimensionSelection(dimension=2, invert=True), # 为其余维度设置固定索引 indices=[ rr.TensorDimensionIndexSelection(dimension=2, index=4), rr.TensorDimensionIndexSelection(dimension=3, index=5), ], # 只为维度 2 显示滑杆;不指定则 indices 里所有维度都会有滑杆 slider=[2], ), # 自定义 colormap、gamma 与放大过滤 scalar_mapping=rrb.TensorScalarMapping( colormap="turbo", gamma=1.5, mag_filter="linear" ), # 填满视图、忽略宽高比 view_fit="fill", ), collapse_panels=True, ) rr.send_blueprint(blueprint)这个示例覆盖了本文前面讲到的全部概念:width/height/indices/slider四个切片参数、colormap/gamma/mag_filter三个标量映射参数,以及view_fit。运行后即可在视口中看到该四维张量在当前索引组合下的 2D 切片图像,并可通过滑杆交互切换batch/channel等维度的索引。
七、测试验证:行为如何被保证
该 crate 的测试既能验证渲染管线,也能验证数据转换的正确性:
- 1D 张量渲染(tests/tensor_1d.rs):记录 100 个 f32 标量构成的 1D 张量,创建根通配的 TensorView 并截图快照(
tensor_1d),验证一维数据也能被展示; - 2D 张量渲染(tests/tensor_2d.rs):用
(i ^ j)生成经典的 XOR 测试纹理(16×16 与 8×8 两个张量),分别在tensors/t1、tensors/t2、tensors(同时命中两个)与根实体四种 origin 下截图(tensor_2d_t1、tensor_2d_t2、tensor_2d_both、tensor_2d_root)——其中同时命中两个张量的场景会触发视图的"一次只能显示一个张量"错误提示路径; - 数据转换(src/tensor_tests.rs):验证
TensorData与ndarray::ArrayViewD的双向转换、三维张量的逐元素索引正确性(z * 4 * 5 + y * 5 + x的内存布局)、形状不匹配与类型不匹配时的TensorCastError等。
测试对应的快照文件位于 crates/viewer/re_view_tensor/tests/snapshots/,可以直接查看 Tensor View 在不同维度输入下的真实渲染效果。
八、已知限制与使用提示
从 view_class.rs 的实现可以看出一个明确限制:每个 Tensor View 一次只能显示一个张量。如果查询结果命中多个张量实体,视图会显示错误提示:
Can only show one tensor at a time; was given N. Update the query so that it returns a single tensor entity and create additional views for the others.
因此在使用时需要注意:
- 通过 Blueprint 中 View 的 origin / 查询过滤器(例如
$origin/**)把范围收敛到单个张量实体; - 需要同时查看多个张量时,为每个张量单独创建 TensorView(从
spawn_heuristics的实现看,自动生成视图时也会"每个张量一个视图",见 view_class.rs); - 另外如前文所述,
U64/I64/F64类型的张量在 GPU 渲染阶段会被收窄为 f32,对精度敏感的场景需要留意。
结语
re_view_tensor以"任意维度张量 → 2D 切片 + colormap"这一简洁抽象,配合完善的启发式默认值、防御式的合法性修正与拖拽式维度映射 UI,为 Rerun 生态提供了实用的张量可视化能力。无论是想要快速浏览神经网络的中间特征图,还是深入调教"哪个维度做宽、哪个维度做高、其余维度取哪个索引",理解本文介绍的slice_selection/scalar_mapping/view_fit三组蓝图属性及其背后的实现逻辑,都能让你在 Rerun 中高效地观察和排查高维数据。
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考