单目3D初始代码
This commit is contained in:
398
tools/scripts_for_gt/visualization/FLOW_ANALYSIS.md
Executable file
398
tools/scripts_for_gt/visualization/FLOW_ANALYSIS.md
Executable file
@@ -0,0 +1,398 @@
|
||||
# 真值可视化流程分析文档
|
||||
|
||||
## 一、完整数据流程分析
|
||||
|
||||
### 1.1 从文件到内存的加载流程
|
||||
|
||||
```
|
||||
原始文件
|
||||
└─> load_label() [dataloaders3d.py:1625]
|
||||
│
|
||||
├─> 解析标签文本文件
|
||||
│ └─> 支持3种格式:
|
||||
│ ├─ 2D only (6 dims)
|
||||
│ ├─ 2D+3D simple (18 dims)
|
||||
│ └─ 2D+3D+faces (50 dims)
|
||||
│
|
||||
└─> 返回 (N, 47) numpy数组
|
||||
└─> 列结构:
|
||||
[0]: class
|
||||
[1-4]: xywh (normalized)
|
||||
[5]: score
|
||||
[6-8]: xyz3d (camera coords)
|
||||
[9-11]: dimensions (l,h,w)
|
||||
[12]: rot_y
|
||||
[13-14]: center_2d_uv (normalized)
|
||||
[15]: alpha
|
||||
[16-23]: front_face [xyz, alpha, uv, score, visible]
|
||||
[24-31]: rear_face
|
||||
[32-39]: left_face
|
||||
[40-47]: right_face
|
||||
```
|
||||
|
||||
### 1.2 ROI变换流程
|
||||
|
||||
```
|
||||
原始标签 (N, 47)
|
||||
└─> post_process_labels_to_roi() [dataloaders3d.py:1060]
|
||||
│
|
||||
├─> 步骤1: 计算ROI区域
|
||||
│ └─> vanish_y = cy - fy * tan(pitch)
|
||||
│ └─> crop_center = (oriW/2, vanish_y)
|
||||
│ └─> roi_bounds = [x1, y1, x2, y2]
|
||||
│
|
||||
├─> 步骤2: 转换2D边界框
|
||||
│ └─> xywhn -> xyxy (absolute)
|
||||
│ └─> shift to ROI coords
|
||||
│ └─> clip to ROI bounds
|
||||
│ └─> normalize to ROI size
|
||||
│
|
||||
├─> 步骤3: 转换UV坐标
|
||||
│ └─> 对每个面的中心投影点 (5对UV)
|
||||
│ └─> u_roi = (u_orig * oriW - roi_x1) / roi_w
|
||||
│ └─> v_roi = (v_orig * oriH - roi_y1) / roi_h
|
||||
│
|
||||
└─> 步骤4: 过滤无效框
|
||||
└─> 移除完全在ROI外的框
|
||||
└─> 标记部分可见的框(cut-in/cut-out)
|
||||
```
|
||||
|
||||
### 1.3 深度归一化流程
|
||||
|
||||
```
|
||||
ROI标签 (N, 47)
|
||||
└─> scale_z3d() [dataloaders3d.py:1518]
|
||||
│
|
||||
├─> 计算缩放因子
|
||||
│ └─> scale = virtual_fx / fx_roi
|
||||
│
|
||||
├─> 缩放Z坐标 (仅Z,不包括X,Y)
|
||||
│ ├─> 列7: z3d_center *= scale
|
||||
│ ├─> 列17: z3d_front *= scale
|
||||
│ ├─> 列25: z3d_rear *= scale
|
||||
│ ├─> 列33: z3d_left *= scale
|
||||
│ └─> 列41: z3d_right *= scale
|
||||
│
|
||||
└─> 原理说明:
|
||||
投影公式: u = fx * X/Z + cx
|
||||
同时缩放fx和Z -> 投影不变
|
||||
X和Y从UV和Z恢复,无需缩放
|
||||
```
|
||||
|
||||
## 二、可视化实现流程
|
||||
|
||||
### 2.1 2D可视化流程
|
||||
|
||||
```
|
||||
标签数组 (N, 47)
|
||||
└─> plot_2d_boxes()
|
||||
│
|
||||
├─> 图像预处理
|
||||
│ └─> resize(scale_factor=2) -> 更清晰
|
||||
│ └─> BGR -> RGB for Annotator
|
||||
│
|
||||
├─> 遍历每个标签
|
||||
│ ├─> 提取: cls, xywh
|
||||
│ ├─> 转换: xywhn -> xyxy (pixel)
|
||||
│ ├─> 绘制: box + label
|
||||
│ └─> 颜色: colors(cls)
|
||||
│
|
||||
└─> 后处理
|
||||
└─> RGB -> BGR
|
||||
└─> 添加标签文字 (cv2.putText)
|
||||
```
|
||||
|
||||
### 2.2 3D可视化流程(车辆类)
|
||||
|
||||
```
|
||||
标签 (47,) + calib
|
||||
└─> decode_and_reconstruct_3d_box_from_target() [plots.py:908]
|
||||
│
|
||||
├─> 步骤1: 恢复原始深度
|
||||
│ └─> z_original = z_normalized * depth_scale
|
||||
│ └─> depth_scale = fx_roi / virtual_fx
|
||||
│
|
||||
├─> 步骤2: 选择最佳可见面
|
||||
│ └─> 遍历4个面 (front, rear, left, right)
|
||||
│ └─> 选择: visible==1 && score>0.3 && 最高score
|
||||
│
|
||||
├─> 步骤3: 从面重建3D中心
|
||||
│ └─> _reconstruct_3d_box_from_face()
|
||||
│ ├─> uv -> 射线方向
|
||||
│ │ └─> X = (u - cx) * Z / fx
|
||||
│ │ └─> Y = (v - cy) * Z / fy
|
||||
│ │
|
||||
│ ├─> 根据面类型计算偏移
|
||||
│ │ └─> front: center = face - [l/2, 0, 0]
|
||||
│ │ └─> rear: center = face + [l/2, 0, 0]
|
||||
│ │ └─> left: center = face - [0, 0, w/2]
|
||||
│ │ └─> right: center = face + [0, 0, w/2]
|
||||
│ │
|
||||
│ └─> 计算8个角点
|
||||
│ └─> compute_3d_box_corners_4face()
|
||||
│
|
||||
└─> 返回: {corners_3d, face_center_2d, face_color, ...}
|
||||
```
|
||||
|
||||
### 2.3 3D可视化流程(行人类)
|
||||
|
||||
```
|
||||
标签 (47,) + calib
|
||||
└─> decode_and_reconstruct_3d_box_from_target() [plots.py:908]
|
||||
│
|
||||
├─> 步骤1: 恢复原始深度
|
||||
│ └─> z_original = z_normalized * depth_scale
|
||||
│
|
||||
├─> 步骤2: 提取3D参数
|
||||
│ └─> center = [x3d, y3d, z3d]
|
||||
│ └─> dims = [l, h, w]
|
||||
│ └─> rot_y
|
||||
│
|
||||
├─> 步骤3: 计算8个角点
|
||||
│ └─> compute_3d_box_corners_4face()
|
||||
│ └─> 基于center, dims, rot_y
|
||||
│ └─> 注意: GT不需要corner reordering
|
||||
│
|
||||
└─> 返回: {corners_3d, center_2d, object_3d, ...}
|
||||
```
|
||||
|
||||
### 2.4 投影和绘制流程
|
||||
|
||||
```
|
||||
decoded_results + image + calib
|
||||
└─> plot_3d_boxes_from_decoded_targets() [plots.py:1343]
|
||||
│
|
||||
├─> 图像预处理
|
||||
│ └─> tensor -> numpy
|
||||
│ └─> normalize -> [0,255]
|
||||
│ └─> resize(scale_factor=2)
|
||||
│
|
||||
├─> 标定参数缩放
|
||||
│ └─> fx, fy, cx, cy *= scale_factor
|
||||
│
|
||||
├─> 遍历每个decoded对象
|
||||
│ └─> draw_3d_box_from_corners() [plots.py:1198]
|
||||
│ │
|
||||
│ ├─> 步骤1: 3D角点 -> 2D投影
|
||||
│ │ └─> u = fx * X/Z + cx
|
||||
│ │ └─> v = fy * Y/Z + cy
|
||||
│ │ └─> 应用畸变校正 (如果有)
|
||||
│ │
|
||||
│ ├─> 步骤2: 绘制12条边
|
||||
│ │ ├─> 前面4条边: 红色/face_color
|
||||
│ │ ├─> 后面4条边: 绿色
|
||||
│ │ └─> 连接边4条: 蓝色
|
||||
│ │
|
||||
│ └─> 步骤3: 标记面中心 (车辆)
|
||||
│ └─> circle at face_center_2d
|
||||
│
|
||||
└─> 后处理
|
||||
└─> 添加标签文字
|
||||
└─> BGR -> RGB
|
||||
```
|
||||
|
||||
### 2.5 BEV可视化流程
|
||||
|
||||
```
|
||||
object_3d = [x, y, z, l, h, w, rot_y, face_type]
|
||||
└─> drawbev() [plots.py:2404]
|
||||
│
|
||||
├─> 坐标系转换
|
||||
│ └─> 相机坐标 -> BEV坐标
|
||||
│ └─> bev_x = z (深度 -> 前后)
|
||||
│ └─> bev_y = -x (右 -> 左右)
|
||||
│
|
||||
├─> 计算4个角点 (俯视)
|
||||
│ └─> 基于 (bev_x, bev_y), l, w, rot_y
|
||||
│
|
||||
├─> 投影到图像坐标
|
||||
│ └─> scale: 200m -> 800px
|
||||
│ └─> origin: bottom-center
|
||||
│
|
||||
└─> 绘制
|
||||
├─> 填充矩形: 半透明
|
||||
├─> 边框: 实线
|
||||
├─> 方向指示: 箭头
|
||||
└─> 颜色: GT=green, Pred=red
|
||||
```
|
||||
|
||||
## 三、关键坐标变换
|
||||
|
||||
### 3.1 归一化坐标 ↔ 像素坐标
|
||||
|
||||
```python
|
||||
# 2D边界框
|
||||
xywh_norm -> xyxy_pixel:
|
||||
x1 = (x - w/2) * img_width
|
||||
y1 = (y - h/2) * img_height
|
||||
x2 = (x + w/2) * img_width
|
||||
y2 = (y + h/2) * img_height
|
||||
|
||||
# UV坐标
|
||||
uv_norm -> uv_pixel:
|
||||
u_pixel = u_norm * img_width
|
||||
v_pixel = v_norm * img_height
|
||||
```
|
||||
|
||||
### 3.2 相机坐标 ↔ 图像坐标
|
||||
|
||||
```python
|
||||
# 投影(3D -> 2D)
|
||||
u = fx * X/Z + cx
|
||||
v = fy * Y/Z + cy
|
||||
|
||||
# 反投影(2D + Z -> 3D)
|
||||
X = (u - cx) * Z / fx
|
||||
Y = (v - cy) * Z / fy
|
||||
|
||||
# 注意:
|
||||
# - 需要已知深度Z
|
||||
# - 可能需要畸变校正
|
||||
```
|
||||
|
||||
### 3.3 ROI坐标变换
|
||||
|
||||
```python
|
||||
# 原始图像 -> ROI图像
|
||||
u_roi = (u_orig - roi_x1) / roi_width
|
||||
v_roi = (v_orig - roi_y1) / roi_height
|
||||
|
||||
# ROI图像 -> 原始图像
|
||||
u_orig = u_roi * roi_width + roi_x1
|
||||
v_orig = v_roi * roi_height + roi_y1
|
||||
|
||||
# 3D坐标:
|
||||
# - xyz3d保持不变(相机坐标系)
|
||||
# - 仅z3d需要深度归一化
|
||||
```
|
||||
|
||||
### 3.4 深度归一化变换
|
||||
|
||||
```python
|
||||
# 训练时(原始 -> 归一化)
|
||||
z_normalized = z_original * (virtual_fx / fx_original)
|
||||
|
||||
# 推理/可视化时(归一化 -> 原始)
|
||||
z_original = z_normalized * (fx_original / virtual_fx)
|
||||
= z_normalized * depth_scale
|
||||
|
||||
# 原理:
|
||||
# 投影: u = fx * X/Z + cx
|
||||
# 缩放: u' = (fx*s) * X/(Z*s) + cx = u
|
||||
# 即:同时缩放fx和Z保持投影不变
|
||||
```
|
||||
|
||||
## 四、常见问题与注意事项
|
||||
|
||||
### 4.1 深度值处理
|
||||
|
||||
**问题**: 为什么只缩放z3d,不缩放x3d和y3d?
|
||||
|
||||
**答案**:
|
||||
- 投影公式: `u = fx * X/Z + cx`
|
||||
- X和Y是从UV和Z反推的: `X = (u-cx)*Z/fx`
|
||||
- 缩放Z后,如果缩放fx,投影保持不变
|
||||
- X和Y会自动适配新的Z值,无需单独缩放
|
||||
|
||||
### 4.2 角点顺序
|
||||
|
||||
**问题**: GT和预测的角点顺序一样吗?
|
||||
|
||||
**答案**:
|
||||
- GT: 直接使用`compute_3d_box_corners_4face()`输出
|
||||
- 预测: 需要根据`rot_y`的符号reorder角点
|
||||
- 在可视化时需要注意这个差异
|
||||
|
||||
### 4.3 坐标系统
|
||||
|
||||
**相机坐标系** (右手系):
|
||||
```
|
||||
Y (down)
|
||||
|
|
||||
|_____ X (right)
|
||||
/
|
||||
Z (forward, depth)
|
||||
```
|
||||
|
||||
**图像坐标系**:
|
||||
```
|
||||
(0,0)_____ u (right)
|
||||
|
|
||||
|
|
||||
v (down)
|
||||
```
|
||||
|
||||
**BEV坐标系** (俯视):
|
||||
```
|
||||
bev_x (depth) ↑
|
||||
|
|
||||
|
|
||||
bev_y <———————·
|
||||
(left)
|
||||
```
|
||||
|
||||
### 4.4 畸变处理
|
||||
|
||||
当标定参数包含`distort_coeffs`时:
|
||||
- ROI模式: 保留畸变系数(需要在可视化时处理)
|
||||
- 虚拟相机模式: 已经去畸变(`distort_coeffs=[]`)
|
||||
|
||||
### 4.5 类别差异
|
||||
|
||||
不同类别使用不同的重建策略:
|
||||
- **车辆(0, 13)**: 基于最佳可见面
|
||||
- **行人(1, 2, 3)**: 基于完整3D框
|
||||
|
||||
原因:
|
||||
- 车辆可能被部分遮挡或出画,面策略更鲁棒
|
||||
- 行人尺寸小,完整框更简单准确
|
||||
|
||||
## 五、调试技巧
|
||||
|
||||
### 5.1 检查标签加载
|
||||
|
||||
```python
|
||||
labels = load_label_file(label_path)
|
||||
print(f"标签数量: {len(labels)}")
|
||||
print(f"类别分布: {np.unique(labels[:, 0])}")
|
||||
print(f"第一个标签:\n{labels[0]}")
|
||||
```
|
||||
|
||||
### 5.2 检查ROI变换
|
||||
|
||||
```python
|
||||
print(f"原始标签数量: {len(labels_orig)}")
|
||||
print(f"ROI后标签数量: {len(labels_roi)}")
|
||||
|
||||
# 检查第一个框的变换
|
||||
if len(labels_roi) > 0:
|
||||
print(f"原始xywh: {labels_orig[0, 1:5]}")
|
||||
print(f"ROI xywh: {labels_roi[0, 1:5]}")
|
||||
```
|
||||
|
||||
### 5.3 检查深度值
|
||||
|
||||
```python
|
||||
# 打印深度值范围
|
||||
z_values = labels[:, 7][~np.isnan(labels[:, 7])]
|
||||
print(f"深度范围: [{z_values.min():.2f}, {z_values.max():.2f}]")
|
||||
print(f"平均深度: {z_values.mean():.2f}")
|
||||
```
|
||||
|
||||
### 5.4 检查解码结果
|
||||
|
||||
```python
|
||||
decoded = decode_and_reconstruct_3d_box_from_target(...)
|
||||
if decoded:
|
||||
print(f"类别: {decoded['cls']}")
|
||||
print(f"角点形状: {decoded['corners_3d'].shape if decoded['corners_3d'] is not None else None}")
|
||||
if decoded.get('object_3d'):
|
||||
print(f"3D位置: {decoded['object_3d'][:3]}")
|
||||
```
|
||||
|
||||
## 六、性能优化建议
|
||||
|
||||
1. **批量处理**: 使用`visualize_batch.py`而不是循环调用单帧脚本
|
||||
2. **分辨率控制**: 调整`scale_factor`参数控制输出图像大小
|
||||
3. **选择性可视化**: 根据需要选择2D、3D或BEV可视化
|
||||
4. **并行处理**: 可以使用多进程批量处理独立的图像
|
||||
Reference in New Issue
Block a user