真值可视化脚本说明
概述
本目录包含用于单帧图像真值可视化的脚本,可以实现2D和3D边界框的可视化。
真值读取到可视化的完整流程
1. 数据加载流程(utils/dataloaders3d.py)
1.1 标签读取 (load_label())
# 标签文件格式(47维数组):
# - 列0: class (类别ID)
# - 列1-4: x, y, w, h (归一化的2D边界框中心和宽高)
# - 列5: score (置信度)
# - 列6-8: x3d, y3d, z3d (3D中心点在相机坐标系下的坐标)
# - 列9-11: length, height, width (3D框的尺寸)
# - 列12: rot_y (偏航角旋转)
# - 列13-14: xc, yc (3D框中心投影到2D的归一化坐标)
# - 列15: alpha (观测角)
# - 列16-23: 前面信息 [x3d, y3d, z3d, alpha, xc, yc, score, visible]
# - 列24-31: 后面信息
# - 列32-39: 左面信息
# - 列40-47: 右面信息
支持三种标签格式:
- 2D only (6列): 仅包含2D边界框
- 2D+3D simple (18列): 针对行人、自行车、骑手,包含基本3D信息
- 2D+3D+faces (50列): 针对车辆、三轮车,包含完整的4个面信息
1.2 图像读取 (load_image())
# 使用cv2.imread读取BGR格式图像
img = cv2.imread(image_path) # (H, W, 3) BGR
1.3 标定参数读取
# 从 camera4.json 读取相机内参
{
"focal_u": fx, # x方向焦距
"focal_v": fy, # y方向焦距
"cu": cx, # x方向主点
"cv": cy, # y方向主点
"pitch": pitch, # 相机俯仰角
"distort_coeffs": [] # 畸变系数
}
2. 数据预处理流程
2.1 ROI变换(可选)
# 计算消失点和裁剪中心
vanish_y = cy - fy * tan(pitch * π/180)
crop_center = (oriW/2, vanish_y)
# 计算ROI边界
roi_x1 = crop_center_x - roi_width/2
roi_y1 = crop_center_y - roi_height/2
roi_x2 = roi_x1 + roi_width
roi_y2 = roi_y1 + roi_height
# 裁剪图像
img_roi = img[roi_y1:roi_y2, roi_x1:roi_x2]
2.2 标签坐标转换
# 将2D边界框从原始图像坐标转换到ROI坐标
# 1. 归一化xywh -> 绝对xyxy
# 2. 平移到ROI相对坐标
# 3. 裁剪到ROI边界内
# 4. 重新归一化
# UV坐标(面中心投影)也需要相应转换
2.3 深度归一化(可选)
# 当使用虚拟焦距时,需要缩放z3d深度值
# 只缩放Z坐标,X和Y从UV和Z恢复
scale = virtual_fx / fx_original
z3d_normalized = z3d_original * scale
# 包括:
# - 列7: 整体中心z3d
# - 列17: 前面中心z3d
# - 列25: 后面中心z3d
# - 列33: 左面中心z3d
# - 列41: 右面中心z3d
3. 可视化流程(test_val_visualize.py)
3.1 2D可视化
# 使用 plot_2d_boxes_to_image() 或类似逻辑
# 1. 放大图像(scale_factor=2)获得更清晰的可视化
# 2. 将归一化坐标转换为像素坐标
# 3. 使用Annotator绘制边界框和类别标签
# 4. 添加标签文字(如"2D GT")
3.2 3D可视化
步骤1:解码3D框 (decode_and_reconstruct_3d_box_from_target())
# 针对不同类别采用不同策略:
# 车辆类(class 0, 13):基于最佳可见面重建
# 1. 遍历4个面(前、后、左、右)
# 2. 选择可见且得分最高的面
# 3. 从面中心(u,v,z)反投影得到3D中心
# 4. 使用dimensions和rot_y计算8个3D角点
# 行人类(class 1, 2, 3):基于完整3D框
# 1. 直接使用(x3d, y3d, z3d)作为3D中心
# 2. 使用dimensions和rot_y计算8个3D角点
# 3. 注意:需要将归一化深度转换回原始尺度
# z3d_original = z3d_normalized * depth_scale
步骤2:投影和绘制 (plot_3d_boxes_from_decoded_targets())
# 1. 将3D角点投影到2D图像平面
# u = fx * X/Z + cx
# v = fy * Y/Z + cy
# 2. 绘制3D框的12条边
# 3. 使用不同颜色区分前后面
# 4. 对于车辆类,标记最佳可见面的中心点
3.3 BEV可视化
# 1. 创建空白BEV图像 (draw_bev_blank)
# 2. 对每个3D框调用 drawbev() 绘制俯视图
# 3. 使用不同颜色区分真值(绿色)和预测(红色)
4. 关键坐标系统
4.1 图像坐标系
- 原点:图像左上角
- x轴:向右
- y轴:向下
- 单位:像素或归一化[0,1]
4.2 相机坐标系
- 原点:相机光心
- x轴:向右
- y轴:向下
- z轴:向前(深度方向)
- 单位:米
4.3 投影关系
u = fx * X/Z + cx
v = fy * Y/Z + cy
# 反投影(已知u,v,Z求X,Y):
X = (u - cx) * Z / fx
Y = (v - cy) * Z / fy
使用方法
基本用法(不应用ROI)
python scripts_for_gt/visualize_single_frame.py \
--image /path/to/images/frame_001.jpg \
--label /path/to/labels/frame_001.txt \
--output ./gt_visualization
带ROI变换(与训练一致)
python scripts_for_gt/visualize_single_frame.py \
--image /path/to/images/frame_001.jpg \
--label /path/to/labels/frame_001.txt \
--output ./gt_visualization \
--roi 704 352 \
--virtual-fx 500 \
--ori-img-size 1920 1080
参数说明
--image: 图像文件路径(必需)--label: 标签文件路径(必需)--output: 输出目录(默认:./gt_visualization)--calib: 相机标定文件路径(camera4.json),如不提供会自动从图像路径推断--roi: ROI尺寸(宽 高),如:--roi 704 352--virtual-fx: 虚拟焦距,用于深度归一化--ori-img-size: 原始图像尺寸(宽 高),如不提供则从图像自动获取
输出文件
脚本会在输出目录生成以下文件:
{filename}_2d_gt.jpg: 2D边界框可视化{filename}_3d_gt.jpg: 3D边界框投影可视化{filename}_bev_gt.jpg: 鸟瞰图(BEV)可视化{filename}_combined.jpg: 组合可视化(2D + 3D + BEV)
示例
假设数据结构如下:
/data/
├── images/
│ └── frame_001.jpg
├── labels/
│ └── frame_001.txt
└── calib/
└── L2_calib/
└── camera4.json
运行命令:
python scripts_for_gt/visualize_single_frame.py \
--image /data/images/frame_001.jpg \
--label /data/labels/frame_001.txt \
--output ./output \
--roi 704 352 \
--virtual-fx 500
注意事项
-
标定文件自动推断:如果不指定
--calib,脚本会自动查找与图像路径对应的标定文件(../calib/L2_calib/camera4.json) -
深度归一化:当使用
--virtual-fx时,z3d坐标会被缩放以匹配虚拟焦距,这与训练时的处理一致 -
坐标系统:
- 输入标签使用归一化坐标[0,1]
- 3D坐标使用相机坐标系(米)
- 深度归一化后需要在可视化时转换回原始尺度
-
支持的类别:
- 0: vehicle(车辆)- 使用面策略
- 1: pedestrian(行人)- 使用完整框
- 2: bicycle(自行车)- 使用完整框
- 3: rider(骑手)- 使用完整框
- 13: tricycle(三轮车)- 使用面策略
快速测试
提供了一个自动测试脚本,可以自动从验证集中找到第一个有效样本并进行可视化:
# 使用默认数据集配置
python scripts_for_gt/test_visualization.py
# 指定数据集配置
python scripts_for_gt/test_visualization.py --data data/mono3d.yaml --output ./test_output
测试脚本会:
- 从数据集配置中读取验证集路径
- 自动查找第一个有标签的样本
- 分别测试原始图像可视化和ROI变换可视化
- 生成所有可视化结果到指定目录
批量处理
如果需要批量处理多帧图像,可以使用批量脚本:
python scripts_for_gt/visualize_batch.py \
--image-dir /path/to/images \
--label-dir /path/to/labels \
--output ./batch_output \
--roi 704 352 \
--virtual-fx 500 \
--max-samples 10
参数说明:
--image-dir: 图像目录--label-dir: 标签目录--output: 输出目录--calib-dir: 标定文件目录(可选)--roi: ROI尺寸--virtual-fx: 虚拟焦距--max-samples: 最大处理样本数(可选)
条件过滤可视化
如果需要从训练集或验证集中筛选特定条件的样本进行可视化,可以使用过滤脚本:
示例1: 可视化包含两轮车的样本(空间范围过滤)
# 只可视化两轮车在左右各10米、纵向50米范围内的样本
python scripts_for_gt/visualize_filtered_train.py \
--data data/mono3d.yaml \
--target-class bicycle \
--x-range -10 10 \
--z-range 0 50 \
--max-samples 20 \
--output ./bicycle_samples
示例2: 可视化包含行人的样本
# 可视化前50个包含行人的训练样本
python scripts_for_gt/visualize_filtered_train.py \
--data data/mono3d.yaml \
--target-class pedestrian \
--max-samples 50 \
--output ./pedestrian_samples
示例3: 可视化特定空间范围内的车辆
# 可视化车辆在左右5米、纵向10-30米范围内的样本
python scripts_for_gt/visualize_filtered_train.py \
--data data/mono3d.yaml \
--target-class vehicle \
--x-range -5 5 \
--z-range 10 30 \
--max-samples 30 \
--output ./vehicle_samples
过滤参数说明
--target-class: 目标类别(vehicle, pedestrian, bicycle, rider, motorcycle, tricycle)--x-range MIN MAX: X坐标范围(左右方向),单位:米--y-range MIN MAX: Y坐标范围(上下方向),单位:米--z-range MIN MAX: Z坐标范围(前后方向/深度),单位:米--min-targets: 最少目标数量(默认1)--max-samples: 最大可视化样本数--split: 数据集划分(train或val,默认train)--use-roi: 使用ROI变换
坐标系说明
相机坐标系:
- X轴: 向右为正(单位:米)
- Y轴: 向下为正(单位:米)
- Z轴: 向前为正(深度方向,单位:米)
示例:
--x-range -10 10: 左右各10米范围--z-range 0 50: 前方0到50米范围--y-range -2 1: 相机水平面上下2米到下方1米范围
脚本说明
本目录包含以下脚本:
-
visualize_single_frame.py - 单帧可视化脚本
- 核心功能:读取单帧图像和标签,生成2D、3D和BEV可视化
- 支持ROI变换和深度归一化
-
visualize_batch.py - 批量可视化脚本
- 批量处理多帧图像
- 自动匹配图像和标签文件
-
test_visualization.py - 快速测试脚本
- 自动查找验证集样本
- 测试不同配置的可视化效果
扩展功能
可以基于此脚本进行扩展:
- 添加不同的可视化风格
- 输出3D框参数到文件
- 与预测结果对比
- 统计分析(深度分布、尺寸分布等)