8.8 KiB
Executable File
convert_merge_tracking_bundle
目录说明
这个目录整理了 convert_merge_tracking.py 及其运行依赖,并额外包含了 make_jl.py,方便在独立目录中完成以下两步:
- 把
merge_tracking.json转成ObjectPerceptionObjectList的 protobuf 三件套。 - 再把三件套转成便于查看的 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/*.py:protobuf 生成代码,提供ObjectList、Object等消息定义。requirements.txt:这个独立目录所需的最小第三方 Python 依赖。
环境准备
建议使用 Python 3。
安装依赖:
pip install -r requirements.txt
requirements.txt 中只有两个最小外部依赖:
protobuf>=3.6.1,<4:用于加载当前目录中的*_pb2.py生成代码。absl-py>=0.7:proto_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.binObjectPerceptionObjectList.index.jsonObjectPerceptionObjectList.data.json
这三个文件的关系如下:
*.bin:连续写入的 protobuf 二进制消息。*.index.json:记录每一帧消息在*.bin里的offset和size。*.data.json:入口配置文件,告诉读取程序去哪里找bin和index。
2) 把 protobuf 三件套转成 JSON 文本
python3 make_jl.py ./out/ObjectPerceptionObjectList.data.json -o ./out/ObjectPerceptionObjectList.jl
如果不加 -o,结果会直接输出到终端:
python3 make_jl.py ./out/ObjectPerceptionObjectList.data.json
说明:
- 虽然脚本命名和注释中把输出叫做
jl或JsonLines,但当前实现会给每条记录加缩进,所以每条记录可能占多行。 - 因此它更准确地说是“逐条追加的 JSON 文本文件”,而不是严格意义上的单行 JSONL。
脚本功能说明
convert_merge_tracking.py
功能:
- 读取
merge_tracking.json。 - 把每一帧的
detections转成ObjectPerceptionObjectListprotobuf。 - 按帧顺序写入二进制文件,并生成配套索引文件和入口描述文件。
适用场景:
- 需要把检测结果接入已有的
ObjectPerceptionObjectList数据链路。 - 需要生成能被
make_jl.py继续消费的中间数据。
make_jl.py
功能:
- 读取
ObjectPerceptionObjectList.data.json。 - 根据
index.json和bin逐条取出 protobuf 消息。 - 将每条
ObjectList转成 JSON 文本并输出到文件或标准输出。
适用场景:
- 想快速查看 protobuf 内容。
- 想把二进制结果转成更易读、更方便后处理的 JSON 文本。
proto_reader/reader.py
功能:
- 解析
data.json中记录的文件位置。 - 读取
index.json。 - 按
offset + size从bin中精确提取每一条 protobuf 消息的原始字节。
它是 make_jl.py 的底层读取器。
每个脚本的实现逻辑
convert_merge_tracking.py 的实现逻辑
1. 加载 protobuf 定义
脚本启动时会把当前目录下的 pyproto 加入 sys.path,然后导入:
object_pb2geometry_pb2camera_pb2
其中真正核心的是 object_pb2,它定义了 Object 和 ObjectList。
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_id或frameId中覆盖帧号。 - 用
--cam-id强制覆盖相机号。
4. 把单个 detection 的公共字段填入 protobuf
populate_object_fields() 负责把一个 detection 的常用字段写入目标 Object,包括:
- 类别和枚举映射
track_idframe_idtimestamplane_assignment- 图像框信息
bbox - 世界坐标下的
pos / size / yaw face_cls对应的姿态- 可选的
model_3d信息
这个函数是整个转换逻辑的公共字段装配器。
5. 构建 Mono3D 量测组件
build_mono_measure_component() 会从 object_3d_ego 中取出:
x, y, zl, h, wyaw
再构造一个 measure_type = kMeasureMono3D 的 Object,作为量测组件挂到上层对象里。
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 - 找到其中声明的
index和data - 拼出真实的
index.json和bin路径
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]
后续真正读取数据时主要使用 offset 和 size。
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