Files
2026-06-24 09:35:46 +08:00

8.8 KiB
Executable File
Raw Permalink Blame History

convert_merge_tracking_bundle

目录说明

这个目录整理了 convert_merge_tracking.py 及其运行依赖,并额外包含了 make_jl.py,方便在独立目录中完成以下两步:

  1. merge_tracking.json 转成 ObjectPerceptionObjectList 的 protobuf 三件套。
  2. 再把三件套转成便于查看的 JSON 文本输出。

目录中的关键文件:

  • convert_merge_tracking.py:把 merge_tracking.json 转成 protobuf 输出。
  • make_jl.py:把 ObjectPerceptionObjectList.data.json 对应的数据转成 JSON 文本。
  • proto_reader/reader.py:按 data.json + index.json + bin 的组合方式读取二进制消息。
  • pyproto/*.pyprotobuf 生成代码,提供 ObjectListObject 等消息定义。
  • requirements.txt:这个独立目录所需的最小第三方 Python 依赖。

环境准备

建议使用 Python 3。

安装依赖:

pip install -r requirements.txt

requirements.txt 中只有两个最小外部依赖:

  • protobuf>=3.6.1,<4:用于加载当前目录中的 *_pb2.py 生成代码。
  • absl-py>=0.7proto_reader/reader.py 中使用了 absl.logging

之所以把 protobuf 限制在 <4,是因为当前打包的 pyproto/*.py 是旧风格生成代码,和 4.x 及以上版本可能不兼容。

使用方法

1) 把 merge_tracking.json 转成 protobuf 三件套

python3 convert_merge_tracking.py /path/to/merge_tracking.json -o ./out

如果需要强制指定相机 ID

python3 convert_merge_tracking.py /path/to/merge_tracking.json -o ./out --cam-id 4

执行后会在输出目录生成:

  • ObjectPerceptionObjectList.bin
  • ObjectPerceptionObjectList.index.json
  • ObjectPerceptionObjectList.data.json

这三个文件的关系如下:

  • *.bin:连续写入的 protobuf 二进制消息。
  • *.index.json:记录每一帧消息在 *.bin 里的 offsetsize
  • *.data.json:入口配置文件,告诉读取程序去哪里找 binindex

2) 把 protobuf 三件套转成 JSON 文本

python3 make_jl.py ./out/ObjectPerceptionObjectList.data.json -o ./out/ObjectPerceptionObjectList.jl

如果不加 -o,结果会直接输出到终端:

python3 make_jl.py ./out/ObjectPerceptionObjectList.data.json

说明:

  • 虽然脚本命名和注释中把输出叫做 jlJsonLines,但当前实现会给每条记录加缩进,所以每条记录可能占多行。
  • 因此它更准确地说是“逐条追加的 JSON 文本文件”,而不是严格意义上的单行 JSONL。

脚本功能说明

convert_merge_tracking.py

功能:

  • 读取 merge_tracking.json
  • 把每一帧的 detections 转成 ObjectPerceptionObjectList protobuf。
  • 按帧顺序写入二进制文件,并生成配套索引文件和入口描述文件。

适用场景:

  • 需要把检测结果接入已有的 ObjectPerceptionObjectList 数据链路。
  • 需要生成能被 make_jl.py 继续消费的中间数据。

make_jl.py

功能:

  • 读取 ObjectPerceptionObjectList.data.json
  • 根据 index.jsonbin 逐条取出 protobuf 消息。
  • 将每条 ObjectList 转成 JSON 文本并输出到文件或标准输出。

适用场景:

  • 想快速查看 protobuf 内容。
  • 想把二进制结果转成更易读、更方便后处理的 JSON 文本。

proto_reader/reader.py

功能:

  • 解析 data.json 中记录的文件位置。
  • 读取 index.json
  • offset + sizebin 中精确提取每一条 protobuf 消息的原始字节。

它是 make_jl.py 的底层读取器。

每个脚本的实现逻辑

convert_merge_tracking.py 的实现逻辑

1. 加载 protobuf 定义

脚本启动时会把当前目录下的 pyproto 加入 sys.path,然后导入:

  • object_pb2
  • geometry_pb2
  • camera_pb2

其中真正核心的是 object_pb2,它定义了 ObjectObjectList

2. 定义映射表

脚本内置了三类映射:

  • CLASS_ID_MAP:把输入里的 class_id 映射到 ObjectType 枚举。
  • ANCHOR_MAP:把 anchor 字符串映射到 AnchorPtInfo 枚举。
  • FACE_CLS_MAP:把 face_cls 映射到 VehiclePose 枚举。

这些映射决定了输入字段如何落到 protobuf 枚举值上。

3. 从输入帧中提取 frame_id 和 cam_id

parse_image_name() 会优先从 image_name 中解析:

  • 帧号 frame_id
  • 相机号 cam_id

同时脚本也支持:

  • 从 detection 的 frame_idframeId 中覆盖帧号。
  • --cam-id 强制覆盖相机号。

4. 把单个 detection 的公共字段填入 protobuf

populate_object_fields() 负责把一个 detection 的常用字段写入目标 Object,包括:

  • 类别和枚举映射
  • track_id
  • frame_id
  • timestamp
  • lane_assignment
  • 图像框信息 bbox
  • 世界坐标下的 pos / size / yaw
  • face_cls 对应的姿态
  • 可选的 model_3d 信息

这个函数是整个转换逻辑的公共字段装配器。

5. 构建 Mono3D 量测组件

build_mono_measure_component() 会从 object_3d_ego 中取出:

  • x, y, z
  • l, h, w
  • yaw

再构造一个 measure_type = kMeasureMono3DObject,作为量测组件挂到上层对象里。

6. 构建最终 Object 的层次结构

build_object() 会生成两层核心对象,并形成嵌套关系:

  • source_obj:表示单目来源对象,包含图像框、模型 3D、Mono3D 量测等信息。
  • obj:最终输出对象,把 source_obj 作为 key_components 挂进去。

如果 object_3d_ego 存在,还会额外挂入一个更底层的 Mono3D 量测 Object

因此最终会形成一种层次化组织:

  • 最终目标对象
  • 来源单目对象
  • Mono3D 量测对象

7. 按帧生成 ObjectList

build_object_list() 对单帧做处理:

  • 创建一个 ObjectList
  • 设置帧号和相机号
  • 遍历该帧所有 detections
  • 调用 build_object() 把每个 detection 追加到 ObjectList.list

如果第一条 detection 里带了 version,也会写到 ObjectList.version

8. 序列化并生成三件套文件

convert() 会遍历所有帧:

  • 把每帧 ObjectList 序列化成字节串
  • 依次写入 ObjectPerceptionObjectList.bin
  • 记录每条消息的 frame_id / offset / size
  • 最后生成 ObjectPerceptionObjectList.index.json
  • 再生成 ObjectPerceptionObjectList.data.json

这样输出结果既能高效存储,也能被后续工具顺序读取。

make_jl.py 的实现逻辑

1. 解析命令行参数

脚本接收:

  • 位置参数 data_json
  • 可选参数 -o / --output

其中 data_json 就是 ObjectPerceptionObjectList.data.json

2. 通过 Reader 定位真实数据文件

make_jsonl() 内部先创建:

reader = pb_reader.Reader(data_json_path)

Reader 会:

  • 读取 data.json
  • 找到其中声明的 indexdata
  • 拼出真实的 index.jsonbin 路径

3. 逐条读取 protobuf 消息

reader.each() 会遍历 index.json 中的每个索引项:

  • 从索引中拿到 offset
  • 从索引中拿到 size
  • bin 中 seek 到对应位置
  • 读取这一条消息的原始字节

4. 把字节反序列化成 ObjectList

读取到的每个 chunk 会被解析成:

object_pb2.ObjectList.FromString(chunk)

这样就把原始二进制恢复为 protobuf 对象。

5. 转成 JSON 并输出

随后脚本调用:

json_format.MessageToDict(objs, including_default_value_fields=True)

把 protobuf 对象转成 Python 字典,并保留默认值字段。

最后再用 json.dumps(..., indent=2) 输出到:

  • 指定文件 -o
  • 或标准输出

proto_reader/reader.py 的实现逻辑

1. 读取 data.json

Reader.__init__() 会先打开 data.json,读取:

  • index[0]
  • data[0]

并把它们解析成相对于 data.json 所在目录的真实路径。

2. 加载 index.json

接着脚本把 index.json 读入内存,保存为 self.object_index

索引中的每一项通常是:

[frame_id, offset, size]

后续真正读取数据时主要使用 offsetsize

3. 迭代返回原始消息字节

each() 打开二进制文件后,会对每条索引执行:

  • seek(offset)
  • read(size)
  • yield bts

因此上层调用方可以把它当作一个“按条产出 protobuf 消息”的生成器。

推荐使用顺序

python3 convert_merge_tracking.py /path/to/merge_tracking.json -o ./out
python3 make_jl.py ./out/ObjectPerceptionObjectList.data.json -o ./out/ObjectPerceptionObjectList.jl

如果你只想验证转换是否成功,优先检查:

  • ./out/ObjectPerceptionObjectList.data.json
  • ./out/ObjectPerceptionObjectList.index.json
  • ./out/ObjectPerceptionObjectList.bin

如果你想看可读结果,再检查:

  • ./out/ObjectPerceptionObjectList.jl