# 真值可视化脚本说明

## 概述

本目录包含用于单帧图像真值可视化的脚本，可以实现2D和3D边界框的可视化。

## 真值读取到可视化的完整流程

### 1. 数据加载流程（utils/dataloaders3d.py）

#### 1.1 标签读取 (`load_label()`)
```python
# 标签文件格式（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()`)
```python
# 使用cv2.imread读取BGR格式图像
img = cv2.imread(image_path)  # (H, W, 3) BGR
```

#### 1.3 标定参数读取
```python
# 从 camera4.json 读取相机内参
{
    "focal_u": fx,      # x方向焦距
    "focal_v": fy,      # y方向焦距  
    "cu": cx,           # x方向主点
    "cv": cy,           # y方向主点
    "pitch": pitch,     # 相机俯仰角
    "distort_coeffs": []  # 畸变系数
}
```

### 2. 数据预处理流程

#### 2.1 ROI变换（可选）
```python
# 计算消失点和裁剪中心
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 标签坐标转换
```python
# 将2D边界框从原始图像坐标转换到ROI坐标
# 1. 归一化xywh -> 绝对xyxy
# 2. 平移到ROI相对坐标
# 3. 裁剪到ROI边界内
# 4. 重新归一化

# UV坐标（面中心投影）也需要相应转换
```

#### 2.3 深度归一化（可选）
```python
# 当使用虚拟焦距时，需要缩放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可视化
```python
# 使用 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()`)
```python
# 针对不同类别采用不同策略：

# 车辆类（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()`)
```python
# 1. 将3D角点投影到2D图像平面
#    u = fx * X/Z + cx
#    v = fy * Y/Z + cy
# 2. 绘制3D框的12条边
# 3. 使用不同颜色区分前后面
# 4. 对于车辆类，标记最佳可见面的中心点
```

#### 3.3 BEV可视化
```python
# 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）

```bash
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变换（与训练一致）

```bash
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
```

运行命令：
```bash
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
```

## 注意事项

1. **标定文件自动推断**：如果不指定`--calib`，脚本会自动查找与图像路径对应的标定文件（`../calib/L2_calib/camera4.json`）

2. **深度归一化**：当使用`--virtual-fx`时，z3d坐标会被缩放以匹配虚拟焦距，这与训练时的处理一致

3. **坐标系统**：
   - 输入标签使用归一化坐标[0,1]
   - 3D坐标使用相机坐标系（米）
   - 深度归一化后需要在可视化时转换回原始尺度

4. **支持的类别**：
   - 0: vehicle（车辆）- 使用面策略
   - 1: pedestrian（行人）- 使用完整框
   - 2: bicycle（自行车）- 使用完整框
   - 3: rider（骑手）- 使用完整框
   - 13: tricycle（三轮车）- 使用面策略

## 快速测试

提供了一个自动测试脚本，可以自动从验证集中找到第一个有效样本并进行可视化：

```bash
# 使用默认数据集配置
python scripts_for_gt/test_visualization.py

# 指定数据集配置
python scripts_for_gt/test_visualization.py --data data/mono3d.yaml --output ./test_output
```

测试脚本会：
1. 从数据集配置中读取验证集路径
2. 自动查找第一个有标签的样本
3. 分别测试原始图像可视化和ROI变换可视化
4. 生成所有可视化结果到指定目录

## 批量处理

如果需要批量处理多帧图像，可以使用批量脚本：

```bash
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: 可视化包含两轮车的样本（空间范围过滤）

```bash
# 只可视化两轮车在左右各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: 可视化包含行人的样本

```bash
# 可视化前50个包含行人的训练样本
python scripts_for_gt/visualize_filtered_train.py \
    --data data/mono3d.yaml \
    --target-class pedestrian \
    --max-samples 50 \
    --output ./pedestrian_samples
```

### 示例3: 可视化特定空间范围内的车辆

```bash
# 可视化车辆在左右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米范围

## 脚本说明

本目录包含以下脚本：

1. **visualize_single_frame.py** - 单帧可视化脚本
   - 核心功能：读取单帧图像和标签，生成2D、3D和BEV可视化
   - 支持ROI变换和深度归一化

2. **visualize_batch.py** - 批量可视化脚本
   - 批量处理多帧图像
   - 自动匹配图像和标签文件

3. **test_visualization.py** - 快速测试脚本
   - 自动查找验证集样本
   - 测试不同配置的可视化效果

## 扩展功能

可以基于此脚本进行扩展：
- 添加不同的可视化风格
- 输出3D框参数到文件
- 与预测结果对比
- 统计分析（深度分布、尺寸分布等）
