Files
yolov26_3d/eval_tools/docs/EVALUATION_DESIGN.md
2026-06-24 09:35:46 +08:00

16 KiB
Executable File
Raw Permalink Blame History

模型输出评测方案设计

1. 评测概述

1.1 评测目标

  • 2D检测评测: 评估所有类别的2D边界框检测性能
  • 3D检测评测: 评估3D类别的空间定位和朝向估计性能

1.2 评测类别划分

  • 3D目标类别 (0-3): vehicle, pedestrian, bike, rider
  • 纯2D目标类别 (4-13): roadblock, head, tsr, guideboard, plate, wheel, tl_border, tl_wick, tl_num, tricycle

2. 数据格式解析

2.1 真值数据格式

2.1.1 3D类别真值格式

完整3D标注车辆类别- 50个值:

[label, x, y, w, h,                          # 0-4: 类别和2D框归一化
 x3d_ori, y3d_ori, z3d_ori,                  # 5-7: 原始3D中心点
 l3d, h3d, w3d,                              # 8-10: 3D尺寸
 rot_y,                                      # 11: 旋转角
 xc_ori, yc_ori,                             # 12-13: 原始中心点2D投影
 xc_ori_d, yc_ori_d,                         # 14-15: 深度相关中心点
 alpha_ori,                                  # 16: 原始alpha角
 0,                                          # 17: 占位符
 # 前面 (18-25)
 x3d_front, y3d_front, z3d_front, alpha_front, xc_front, yc_front, score_front, is_occ_front,
 # 后面 (26-33)
 x3d_back, y3d_back, z3d_back, alpha_back, xc_back, yc_back, score_back, is_occ_back,
 # 左面 (34-41)
 x3d_left, y3d_left, z3d_left, alpha_left, xc_left, yc_left, score_left, is_occ_left,
 # 右面 (42-49)
 x3d_right, y3d_right, z3d_right, alpha_right, xc_right, yc_right, score_right, is_occ_right]

完整3D标注非车辆类别- 18个值:

[label, x, y, w, h,                          # 0-4: 类别和2D框归一化
 x3d_ori, y3d_ori, z3d_ori,                  # 5-7: 3D中心点
 l3d, h3d, w3d,                              # 8-10: 3D尺寸
 rot_y,                                      # 11: 旋转角
 xc_ori, yc_ori,                             # 12-13: 中心点2D投影
 xc_ori_d, yc_ori_d,                         # 14-15: 深度相关中心点
 alpha_ori,                                  # 16: alpha角
 0]                                          # 17: 占位符

仅2D标注 - 6个值:

[label, x, y, w, h, -1]                      # 最后一位为-1表示无3D标注

2.1.2 纯2D类别真值格式6个值

[label, x, y, w, h, -1]                      # label ∈ {4,5,6,7,8,9,10,11,12,13}

2.2 检测结果格式

2.2.1 3D类别检测格式15个值

车辆类别:

vehicle 0.95 368.08 574.17 437.89 617.20 cam -30.14 1.43 68.55 5.52 2.50 2.31 2.70 left
[label, conf, x1, y1, x2, y2, coord_sys, x3d, y3d, z3d, l3d, h3d, w3d, rot_y, face_type]

face_type可以是 front, back, left, right也支持 rear 和 tail 作为 back 的别名

非车辆类别:

pedestrian 0.95 368.08 574.17 437.89 617.20 cam -30.14 1.43 68.55 5.52 2.50 2.31 2.70 whole
[label, conf, x1, y1, x2, y2, coord_sys, x3d, y3d, z3d, l3d, h3d, w3d, rot_y, whole]

2.2.2 纯2D类别检测格式5个值

plate 0.94246 532.12 203.26 558.73 214.86
[label, conf, x1, y1, x2, y2]

3. 评测指标设计

3.1 2D检测指标

3.1.1 基础指标

  • Precision (精确率): TP / (TP + FP)
  • Recall (召回率): TP / (TP + FN)
  • AP (Average Precision): PR曲线下面积IoU阈值=0.5
  • mAP (mean Average Precision): 所有类别AP的平均值

3.1.2 匹配规则

  • IoU阈值: 0.5
  • 匹配策略:
    1. 计算预测框与真值框的IoU
    2. 按置信度从高到低排序预测框
    3. 每个真值框最多匹配一个预测框
    4. IoU >= 0.5 且类别相同视为匹配成功TP
    5. 未匹配的预测框为FP未匹配的真值框为FN

3.1.3 分类别评测

  • 对每个类别分别计算 Precision, Recall, AP
  • 类别包括: vehicle, pedestrian, bike, rider, roadblock, head, tsr, guideboard, plate, wheel, tl_border, tl_wick, tl_num, tricycle

3.1.4 整体评测

  • 总Precision: 所有类别的总TP / (总TP + 总FP)
  • 总Recall: 所有类别的总TP / (总TP + 总FN)
  • mAP: 所有类别AP的算术平均

3.2 3D检测指标

3.2.1 评测范围

仅评测3D类别vehicle, pedestrian, bike, rider

3.2.2 前提条件

只有在2D检测匹配成功IoU >= 0.5且真值包含完整3D标注的情况下才进行3D指标评测

3.2.3 3D评测指标

车辆类别的测距误差计算:

车辆类别需要根据预测结果中的最近面信息front/back/left/right选取真值中对应的最近面中心点进行比较

  1. 根据预测结果中的face_type字段front/back/left/right确定预测的最近面
  2. 从真值的4个面信息中选取对应面的中心点坐标
  3. 计算预测最近面中心点与真值对应面中心点的误差
# 车辆类别
face_mapping = {
    'front': [18, 19, 20],  # x3d_front, y3d_front, z3d_front 在真值中的索引
    'back':  [26, 27, 28],  # x3d_back, y3d_back, z3d_back
    'left':  [34, 35, 36],  # x3d_left, y3d_left, z3d_left
    'right': [42, 43, 44]   # x3d_right, y3d_right, z3d_right
}

# 根据预测的face_type选择真值中对应的面中心点
face_type = det_result['face_type']  # 'front', 'back', 'left', 'right'
x3d_gt, y3d_gt, z3d_gt = gt_values[face_mapping[face_type]]

# 获取预测的最近面中心点
x3d_pred, y3d_pred, z3d_pred = det_result['3d_info']['center']

# 计算误差
lateral_error = |x3d_pred - x3d_gt|
longitudinal_error = |z3d_pred - z3d_gt|

非车辆类别的测距误差计算:

非车辆类别pedestrian, bike, rider直接使用3D框中心点计算误差

# 非车辆类别
x3d_gt, y3d_gt, z3d_gt = gt_values[5:8]  # x3d_ori, y3d_ori, z3d_ori
x3d_pred, y3d_pred, z3d_pred = det_result['3d_info']['center']

lateral_error = |x3d_pred - x3d_gt|
longitudinal_error = |z3d_pred - z3d_gt|

Heading偏差 (Heading Error):

所有3D类别使用相同的方式计算heading误差

heading_error = |normalize_angle(rot_y_pred - rot_y_gt)|

其中 normalize_angle 将角度差归一化到 [-π, π]

3.2.4 统计指标

对每个3D类别分别统计

  • 横向误差: 平均值、中位数、标准差、90%分位数
  • 纵向误差: 平均值、中位数、标准差、90%分位数
  • Heading误差: 平均值、中位数、标准差、90%分位数

4. 评测流程设计

4.1 数据预处理

4.1.1 真值数据解析

def parse_ground_truth(gt_line, img_width, img_height):
    """
    解析真值标注
    返回: {
        'label': int,
        'bbox_2d': [x1, y1, x2, y2],  # 像素坐标
        'has_3d': bool,
        '3d_info': {
            'center': [x3d, y3d, z3d],  # 原始中心点(用于非车辆类别)
            'dimensions': [l3d, h3d, w3d],
            'rotation': rot_y,
            'faces': {  # 仅车辆类别有此字段
                'front': [x3d, y3d, z3d, alpha, xc, yc, score, is_occ],
                'back':  [x3d, y3d, z3d, alpha, xc, yc, score, is_occ],
                'left':  [x3d, y3d, z3d, alpha, xc, yc, score, is_occ],
                'right': [x3d, y3d, z3d, alpha, xc, yc, score, is_occ]
            } if label == 0 else None
        } if has_3d else None
    }
    """

4.1.2 检测结果解析

def parse_detection(det_line):
    """
    解析检测结果
    返回: {
        'label': str -> int,
        'confidence': float,
        'bbox_2d': [x1, y1, x2, y2],
        '3d_info': {
            'center': [x3d, y3d, z3d],
            'dimensions': [l3d, h3d, w3d],
            'rotation': rot_y,
            'face_type': str
        } if is_3d_class else None
    }
    """

4.2 2D评测流程

对每张图像:
  1. 加载真值和检测结果
  2. 对每个类别:
     a. 筛选出该类别的GT和DET
     b. 计算所有配对的IoU矩阵
     c. 按置信度排序DET
     d. 贪婪匹配Hungarian or Greedy
     e. 统计TP, FP, FN
     f. 记录置信度和匹配状态
  
对每个类别:
  3. 根据所有图像的统计:
     a. 按置信度排序所有预测
     b. 计算不同阈值下的Precision-Recall
     c. 计算AP (使用插值或积分)
  
整体统计:
  4. 计算总Precision, Recall
  5. 计算mAP

4.3 3D评测流程

对每张图像:
  1. 基于2D匹配结果
  2. 对每对匹配成功的(GT, DET):
     a. 检查GT是否有完整3D标注
     b. 检查DET是否为3D类别
     c. 如果都满足:
        - 如果是车辆类别(label=0):
          * 根据DET的face_type选择GT中对应面的中心点
          * 计算预测最近面与真值对应面的横向/纵向误差
        - 如果是非车辆类别(label=1,2,3):
          * 直接使用3D框中心点计算横向/纵向误差
        - 计算Heading误差所有类别相同
        - 按类别记录

对每个3D类别:
  3. 统计所有图像的误差:
     - 横向: mean, median, std, 90th percentile
     - 纵向: mean, median, std, 90th percentile
     - Heading: mean, median, std, 90th percentile

5. 实现架构

5.1 模块划分

eval_tools/
├── evaluator/
│   ├── __init__.py
│   ├── parser.py              # 数据解析模块
│   ├── matcher.py             # 2D匹配模块
│   ├── metrics_2d.py          # 2D指标计算
│   ├── metrics_3d.py          # 3D指标计算
│   ├── evaluator.py           # 主评测器
│   └── visualizer.py          # 结果可视化
├── configs/
│   └── eval_config.yaml       # 评测配置
└── eval.py                    # 评测入口脚本

5.2 核心类设计

5.2.1 数据解析器

class GroundTruthParser:
    def parse_line(self, line, img_shape)
    def is_3d_annotated(self, values)
    def get_class_name(self, label_id)

class DetectionParser:
    def parse_line(self, line)
    def map_class_name(self, name_str)

5.2.2 匹配器

class Matcher2D:
    def __init__(self, iou_threshold=0.5)
    def compute_iou(self, box1, box2)
    def match(self, gts, dets)  # 返回匹配对列表

5.2.3 指标计算器

class Metrics2D:
    def __init__(self)
    def add_image_results(self, matches, gts, dets, class_id)
    def compute_ap(self, class_id)
    def compute_map(self), face_type=None)
    def compute_statistics(self, class_id)
    def get_summary(self)
    def _get_vehicle_face_center(self, gt_faces, face_type)  # 根据face_type获取对应面中心
class Metrics3D:
    def __init__(self)
    def add_sample(self, gt_3d, det_3d, class_id)
    def compute_statistics(self, class_id)
    def get_summary(self)

5.2.4 主评测器

class Evaluator:
    def __init__(self, config)
    def load_ground_truth(self, gt_file)
    def load_detections(self, det_file)
    def evaluate_2d(self)
    def evaluate_3d(self)
    def generate_report(self, output_path)

5.3 配置文件示例

# eval_config.yaml
dataset:
  gt_path: "path/to/ground_truth"
  det_path: "path/to/detections"
  image_list: "path/to/image_list.txt"

classes:
  3d_classes: [0, 1, 2, 3]  # vehicle, pedestrian, bike, rider
  2d_classes: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
  class_names:
    0: "vehicle"
    1: "pedestrian"
    2: "bike"
    3: "rider"
    4: "roadblock"
    5: "head"
    6: "tsr"
    7: "guideboard"
    8: "plate"
    9: "wheel"
    10: "tl_border"
    11: "tl_wick"
    12: "tl_num"
    13: "tricycle"

matching:
  iou_threshold: 0.5
  
metrics_2d:
  enabled: true
  confidence_threshold: [0.1, 0.3, 0.5, 0.7, 0.9]
  
metrics_3d:
  enabled: true
  distance_ranges:  # 可选:分距离段统计
    - [0, 30]
    - [30, 60]
    - [60, 100]
    - [100, 999]

output:
  save_path: "eval_results"
  format: ["json", "csv", "txt"]
  visualize: true

6. 输出报告格式

6.1 2D评测报告

{
  "2d_evaluation": {
    "per_class": {
      "vehicle": {
        "precision": 0.92,
        "recall": 0.88,
        "ap": 0.90,
        "num_gt": 1500,
        "num_det": 1450,
        "tp": 1320,
        "fp": 130,
        "fn": 180
      },
      "pedestrian": {...},
      ...
    },
    "overall": {
      "precision": 0.87,
      "recall": 0.84,
      "map": 0.85,
      "num_classes": 14
    }
  }
}

6.2 3D评测报告

{
  "3d_evaluation": {
    "vehicle": {
      "lateral_error": {
        "mean": 0.25,
        "median": 0.18,
        "std": 0.15,
        "percentile_90": 0.45
      },
      "longitudinal_error": {
        "mean": 1.2,
        "median": 0.9,
        "std": 0.8,
        "percentile_90": 2.1
      },
      "heading_error": {
        "mean": 0.08,
        "median": 0.05,
        "std": 0.06,
        "percentile_90": 0.15
      },
      "num_samples": 1320
    },
    "pedestrian": {...},
    ...
  }
}

7. 关键实现细节

7.1 IoU计算

def compute_iou(box1, box2):
    """
    box: [x1, y1, x2, y2]
    """
    x1 = max(box1[0], box2[0])
    y1 = max(box1[1], box2[1])
    x2 = min(box1[2], box2[2])
    y2 = min(box1[3], box2[3])
    
    if x2 < x1 or y2 < y1:
        return 0.0
    
    intersection = (x2 - x1) * (y2 - y1)
    area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
    area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
    union = area1 + area2 - intersection
    
    return intersection / union if union > 0 else 0.0

7.2 AP计算11点插值法

def compute_ap(precisions, recalls):
    """
    使用VOC 11点插值法计算AP
    """
    ap = 0.0
    for t in np.linspace(0, 1, 11):
        if np.sum(recalls >= t) == 0:
            p = 0
        else:
            p = np.max(precisions[recalls >= t])
        ap += p / 11.0
    return ap

7.3 角度归一化

def normalize_angle(angle):
    """
    将角度归一化到[-π, π]
    """
    while angle > np.pi:
        angle -= 2 * np.pi
    while angle < -np.pi:
        angle += 2 * np.pi
    return angle

7.4 坐标系转换

def normalized_to_pixel(bbox_norm, img_width, img_height):
    """
    归一化坐标转像素坐标
    bbox_norm: [x_center, y_center, w, h] (normalized)
    返回: [x1, y1, x2, y2] (pixel)
    """
    x_center = bbox_norm[0] * img_width
    y_center = bbox_norm[1] * img_height
    w = bbox_norm[2] * img_width
    h = bbox_norm[3] * img_height
    
    x1 = x_center - w / 2
    y1 = y_center - h / 2
    x2 = x_center + w / 2
    y2 = y_center + h / 2
    
    return [x1, y1, x2, y2]

8. 使用示例

8.1 命令行使用

# 基础评测
python eval_tools/eval.py \
    --gt-path /path/to/labels \
    --det-path /path/to/predictions \
    --output-dir eval_results

# 指定配置文件
python eval_tools/eval.py \
    --config eval_tools/configs/eval_config.yaml

# 只评测2D
python eval_tools/eval.py \
    --config eval_config.yaml \
    --eval-2d-only

# 只评测3D
python eval_tools/eval.py \
    --config eval_config.yaml \
    --eval-3d-only

8.2 Python API使用

from eval_tools.evaluator import Evaluator

# 创建评测器
evaluator = Evaluator(config_path='eval_config.yaml')

# 加载数据
evaluator.load_data(
    gt_path='/path/to/labels',
    det_path='/path/to/predictions'
)

# 执行评测
results_2d = evaluator.evaluate_2d()
results_3d = evaluator.evaluate_3d()

# 生成报告
evaluator.generate_report(
    output_dir='eval_results',
    formats=['json', 'csv', 'html']
)

9. 扩展性考虑

9.1 多IoU阈值评测

可扩展支持COCO风格的多IoU阈值0.5:0.05:0.95

9.2 距离分段评测

对3D指标按不同距离段分别统计近距离、中距离、远距离

9.3 场景分类评测

可按不同场景(白天/夜晚、晴天/雨天等)分别评测

9.4 时序一致性评测

对视频序列评测跟踪一致性和稳定性

10. 注意事项

  1. 坐标系统一: 确保GT和DET使用相同的坐标系和图像尺寸
  2. 类别映射: 注意字符串类别名和数字ID的映射关系
  3. 边界情况: 处理空检测、空真值、图像尺寸不一致等情况
  4. 性能优化: 对于大规模数据,考虑并行处理和内存优化
  5. 数值精度: 3D指标计算注意浮点数精度问题
  6. 可视化: 提供检测结果和误差分布的可视化,便于分析