# EasyMetrics 使用指南

EasyMetrics 是一个轻量级、零依赖的机器学习指标评估平台，专注于提供简单易用且准确的模型评估工具。

## 📦 安装与配置

### 从 PyPI 安装 (推荐)

EasyMetrics 已发布到 PyPI，可以通过 pip 直接安装：

```bash
pip install EasyMetrics
```

### 从 GitHub 安装

也可以从 GitHub 仓库安装最新版本：

```bash
pip install git+https://github.com/Lucidyn/EasyMetrics.git
```

### 本地安装

如果你需要修改源码或使用开发版本，可以克隆仓库后本地安装：

```bash
# 克隆仓库
git clone https://github.com/Lucidyn/EasyMetrics.git

# 进入目录
cd EasyMetrics

# 安装为可编辑模式
pip install -e .
```

### 依赖项

EasyMetrics 的依赖非常精简：
- **核心依赖**: numpy >= 1.17.0
- **可选依赖**: tqdm (用于进度条功能)

如果需要进度条功能，可以安装 tqdm：

```bash
pip install tqdm
```

## 🚀 核心功能使用

### 1. 目标检测评估

#### 基本用法

```python
import numpy as np
from easyMetrics.tasks.detection import evaluate_detection

# 准备数据 - 每张图片一个字典
preds = [{
    'boxes': np.array([[100, 100, 200, 200]]),  # [x1, y1, x2, y2] 格式
    'scores': np.array([0.9]),                  # 置信度分数
    'labels': np.array([0])                     # 类别索引
}]
targets = [{
    'boxes': np.array([[100, 100, 200, 200]]),  # 真实边界框
    'labels': np.array([0])                     # 真实类别
}]

# 评估并获取结果
results = evaluate_detection(preds, targets)

# 打印关键指标
print(f"mAP: {results['mAP']:.4f}")
print(f"mAP_50: {results['mAP_50']:.4f}")
print(f"mAP_75: {results['mAP_75']:.4f}")
print(f"AR_100: {results['AR_100']:.4f}")
```

#### 高级功能

##### 1.1 寻找最佳置信度阈值

**功能说明**：自动计算满足特定精度要求的最佳置信度阈值，帮助你确定模型上线时的阈值设置。

**使用场景**：
- 要求模型在 IoU=0.5 时精度至少达到 90%
- 需要为不同类别设置不同的阈值

**示例代码**：

```python
# 场景 1: IoU=0.5 时精度至少达到 90%
results = evaluate_detection(
    preds, targets,
    score_criteria=[(0.5, 0.9)]  # 格式: [(iou阈值, 最低精度要求)]
)

# 获取类别 0 的最佳阈值
best_thresh = results.get('BestScore_IoU0.50_P0.90_0')
print(f"类别 0 的推荐阈值: {best_thresh}")

# 场景 2: 多个精度要求
results = evaluate_detection(
    preds, targets,
    score_criteria=[(0.5, 0.9), (0.75, 0.8)]
)

# 获取不同要求下的阈值
best_thresh_50 = results.get('BestScore_IoU0.50_P0.90_0')
best_thresh_75 = results.get('BestScore_IoU0.75_P0.80_0')
```

##### 1.2 并行加速计算

**功能说明**：对于大规模数据集，启用多核并行计算以提高评估速度。

**使用方法**：

```python
# 使用 4 个核心进行评估
results = evaluate_detection(preds, targets, n_jobs=4)

# 使用所有可用核心
results = evaluate_detection(preds, targets, n_jobs=-1)
```

##### 1.3 自定义指标筛选

**功能说明**：只计算你关心的指标，减少输出干扰，提高计算效率。

**使用示例**：

```python
# 只计算 mAP 和 mAP_50
results = evaluate_detection(preds, targets, metrics=['mAP', 'mAP_50'])
print(results)

# 只计算召回率相关指标
results = evaluate_detection(preds, targets, metrics=['AR_100', 'AR_10'])
print(results)

# 只计算特定尺度的指标
results = evaluate_detection(preds, targets, metrics=['mAP_s', 'mAP_m', 'mAP_l'])
print(results)
```

##### 1.4 模型比较与分析

**功能说明**：使用 EasyMetrics 比较不同模型的性能，分析模型在不同场景下的表现。

**使用场景**：
- 比较不同训练策略的模型性能
- 分析模型在不同尺度目标上的表现
- 评估模型在不同 IoU 阈值下的稳定性

**使用示例**：

```python
# 假设我们有两个模型的预测结果
model1_preds = [...]  # 模型 1 的预测
model2_preds = [...]  # 模型 2 的预测
targets = [...]       # 真实标签

# 评估模型 1
results1 = evaluate_detection(model1_preds, targets)

# 评估模型 2
results2 = evaluate_detection(model2_preds, targets)

# 比较模型性能
print("=== 模型性能比较 ===")
print(f"模型 1 mAP: {results1['mAP']:.4f}")
print(f"模型 2 mAP: {results2['mAP']:.4f}")
print(f"性能差异: {(results2['mAP'] - results1['mAP']):.4f}")

# 分析模型在不同尺度上的表现
print("\n=== 模型在不同尺度上的表现 ===")
print(f"模型 1 小目标 mAP_s: {results1['mAP_s']:.4f}")
print(f"模型 2 小目标 mAP_s: {results2['mAP_s']:.4f}")
print(f"模型 1 中目标 mAP_m: {results1['mAP_m']:.4f}")
print(f"模型 2 中目标 mAP_m: {results2['mAP_m']:.4f}")
print(f"模型 1 大目标 mAP_l: {results1['mAP_l']:.4f}")
print(f"模型 2 大目标 mAP_l: {results2['mAP_l']:.4f}")

# 分析模型在不同 IoU 阈值下的表现
print("\n=== 模型在不同 IoU 阈值下的表现 ===")
print(f"模型 1 IoU=0.5: {results1['mAP_50']:.4f}")
print(f"模型 2 IoU=0.5: {results2['mAP_50']:.4f}")
print(f"模型 1 IoU=0.75: {results1['mAP_75']:.4f}")
print(f"模型 2 IoU=0.75: {results2['mAP_75']:.4f}")
```

## 📊 数据格式详解

### 预测结果 (preds)

`preds` 是一个列表，每个元素对应一张图片的预测结果，格式为字典：

| 键名 | 类型 | 形状 | 说明 |
|------|------|------|------|
| `boxes` | numpy.ndarray | (N, 4) | 预测的边界框，格式为 `[x1, y1, x2, y2]` |
| `scores` | numpy.ndarray | (N,) | 每个预测框的置信度分数 |
| `labels` | numpy.ndarray | (N,) | 每个预测框的类别索引 (整数) |

### 真实标签 (targets)

`targets` 是一个列表，每个元素对应一张图片的真实标签，格式为字典：

| 键名 | 类型 | 形状 | 说明 |
|------|------|------|------|
| `boxes` | numpy.ndarray | (M, 4) | 真实的边界框，格式为 `[x1, y1, x2, y2]` |
| `labels` | numpy.ndarray | (M,) | 真实的类别索引 (整数) |

### 数据格式示例

```python
# 两张图片的示例数据
preds = [
    # 图片 1 的预测
    {
        'boxes': np.array([[10, 10, 50, 50], [100, 100, 150, 150]]),
        'scores': np.array([0.95, 0.8]),
        'labels': np.array([0, 1])
    },
    # 图片 2 的预测
    {
        'boxes': np.array([[200, 200, 250, 250]]),
        'scores': np.array([0.9]),
        'labels': np.array([0])
    }
]

targets = [
    # 图片 1 的真实标签
    {
        'boxes': np.array([[10, 10, 50, 50], [100, 100, 150, 150]]),
        'labels': np.array([0, 1])
    },
    # 图片 2 的真实标签
    {
        'boxes': np.array([[200, 200, 250, 250], [300, 300, 350, 350]]),
        'labels': np.array([0, 1])
    }
]
```

## 🔧 扩展与定制

### 添加新任务

要添加新的评估任务（如分类、分割等）：

1. 在 `easyMetrics/tasks/` 下创建新目录（例如 `classification`）
2. 创建评估接口文件（例如 `interface.py`）
3. 继承 `easyMetrics.core.Metric` 基类
4. 实现 `reset()`, `update()` 和 `compute()` 方法

### 自定义匹配策略

EasyMetrics 支持自定义匹配策略，详情请参考源码中的 `matcher.py` 文件。

## 📋 参数详解

### evaluate_detection 函数参数说明

#### 核心参数

| 参数名 | 类型 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- |
| **preds** | List[Any] | 必填 | 每张图片的预测结果列表。支持多种格式，具体取决于 `format` 或 `pred_format` 参数。<br><br>**支持的输入类型**：<br>- COCO 格式字典列表<br>- VOC 格式列表<br>- YOLO 格式列表<br>- 标量、列表、numpy 数组等混合类型 |
| **targets** | List[Any] | 必填 | 每张图片的真实标签列表。支持多种格式，具体取决于 `format` 或 `target_format` 参数。<br><br>**支持的输入类型**：<br>- COCO 格式字典列表<br>- VOC 格式列表<br>- YOLO 格式列表<br>- 标量、列表、numpy 数组等混合类型 |
| **metrics** | Optional[List[str]] | None | 需要返回的特定指标列表。如果为 None，则返回所有计算的指标。<br><br>**常用指标**：<br>- `mAP`: 综合评估指标<br>- `mAP_50`: IoU=0.5 时的 mAP<br>- `mAP_75`: IoU=0.75 时的 mAP<br>- `mAP_s`, `mAP_m`, `mAP_l`: 不同尺度目标的 mAP<br>- `AR_1`, `AR_10`, `AR_100`: 不同最大检测数下的召回率 |
| **n_jobs** | int | 1 | 并行计算线程数。1 表示串行计算，-1 表示使用所有可用 CPU 核心。<br><br>**建议**：<br>- 小数据集：使用默认值 1<br>- 中等数据集：使用 2-4 个核心<br>- 大数据集：使用 -1 或根据系统配置选择合适的核心数 |
| **score_criteria** | Optional[List[Tuple[float, float]]] | None | 计算指定 IoU 和精度下的最佳置信度阈值。格式为 `[(iou_thresh, min_precision), ...]`，例如 `[(0.5, 0.9)]` 表示寻找 IoU=0.5 时精度至少为 0.9 的最低置信度。 |
| **progress** | bool | True | 是否显示进度条。默认为 True，在计算过程中显示评估进度。<br><br>**注意**：进度条功能依赖于 tqdm 库，如果未安装 tqdm，会自动降级为简单的打印输出。 |

#### 格式参数

| 参数名 | 类型 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- |
| **format** | str | "coco" | 输入数据的默认格式，当 `pred_format` 和 `target_format` 未指定时使用。支持 "coco", "voc", "yolo", "custom"。 |
| **pred_format** | Optional[str] | None | 预测结果的格式，优先级高于 `format`。<br><br>**支持的格式**：<br>- "coco": COCO 格式<br>- "voc": VOC 格式<br>- "yolo": YOLO 格式<br>- "custom": 自定义格式 |
| **target_format** | Optional[str] | None | 真实标签的格式，优先级高于 `format`。<br><br>**支持的格式**：<br>- "coco": COCO 格式<br>- "voc": VOC 格式<br>- "yolo": YOLO 格式<br>- "custom": 自定义格式 |

#### 额外参数（**kwargs）

| 参数名 | 类型 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- |
| **image_size** | Tuple[int, int] | (640, 640) | YOLO 格式需要的图像尺寸 (width, height)，用于将归一化坐标转换为绝对坐标。<br><br>**示例**：<br>- `(640, 640)`: 正方形图像<br>- `(1920, 1080)`: 高清图像 |
| **custom_converter** | Callable | None | 自定义格式的转换函数，当 `format` 为 "custom" 时使用。<br><br>**函数签名**：<br>```python
def custom_converter(data, is_prediction):
    """将自定义格式转换为内部标准格式"""
    # 处理逻辑
    return converted_data
``` |

#### 格式说明

##### COCO 格式

**预测结果**：
```python
{
    'boxes': np.array([[x1, y1, x2, y2], ...]),  # 边界框坐标
    'scores': np.array([score1, score2, ...]),     # 置信度分数
    'labels': np.array([label1, label2, ...])      # 类别索引
}
```

**真实标签**：
```python
{
    'boxes': np.array([[x1, y1, x2, y2], ...]),  # 边界框坐标
    'labels': np.array([label1, label2, ...])      # 类别索引
}
```

##### VOC 格式

**预测结果**：
```python
[[x1, y1, x2, y2, class_id, confidence], ...]
```

**真实标签**：
```python
[[x1, y1, x2, y2, class_id], ...]
```

##### YOLO 格式

**预测结果**：
```python
[[class_id, x_center, y_center, width, height, confidence], ...]
```

**真实标签**：
```python
[[class_id, x_center, y_center, width, height], ...]
```

**注意**：YOLO 格式的坐标是归一化的 (0-1)，需要提供 `image_size` 参数进行转换。

#### 示例用法

```python
# 基本用法
evaluate_detection(preds, targets)

# 指定并行计算线程数
evaluate_detection(preds, targets, n_jobs=4)

# 只计算特定指标
evaluate_detection(preds, targets, metrics=['mAP', 'mAP_50'])

# 计算最佳置信度阈值
evaluate_detection(preds, targets, score_criteria=[(0.5, 0.9)])

# 使用不同格式的输入
evaluate_detection(
    preds, targets,
    pred_format="yolo",    # 预测值使用 YOLO 格式
    target_format="voc",    # 真值使用 VOC 格式
    image_size=(640, 480)    # YOLO 格式需要的图像尺寸
)

# 禁用进度条
evaluate_detection(preds, targets, progress=False)
```

## 💡 实用技巧

1. **数据预处理**：确保输入的边界框坐标格式正确（`[x1, y1, x2, y2]`），且为浮点数类型。

2. **类别索引**：确保预测结果和真实标签使用相同的类别索引体系。

3. **性能优化**：
   - 对于大规模数据集，使用 `n_jobs=-1` 启用并行计算
   - 使用 `metrics` 参数只计算需要的指标

4. **结果分析**：
   - 关注 `mAP_50` 指标评估模型在宽松条件下的性能
   - 关注 `mAP_75` 指标评估模型在严格条件下的性能
   - 使用 `AR_100` 评估模型的召回能力

## 🚑 故障排除

### 常见问题与解决方案

#### 1. 导入错误

**问题**：`ModuleNotFoundError: No module named 'easyMetrics'`

**解决方案**：
- 确保已正确安装 EasyMetrics：`pip install EasyMetrics`
- 检查 Python 环境是否正确
- 检查项目结构，确保 easyMetrics 文件夹在正确的位置

#### 2. 数据格式错误

**问题**：`KeyError: 'boxes'` 或 `ValueError: not enough values to unpack`

**解决方案**：
- 检查输入数据格式是否正确
- 确保 `preds` 和 `targets` 是列表格式
- 确保每个元素包含正确的键（如 'boxes'、'scores'、'labels'）
- 对于不同格式的输入，确保指定了正确的 `pred_format` 和 `target_format`

#### 3. 类型错误

**问题**：`TypeError: 'int' object is not iterable` 或 `TypeError: expected sequence or array-like, got int`

**解决方案**：
- 检查输入数据类型是否正确
- 对于标量输入，确保它们可以被正确转换为数组
- 确保 `boxes`、`scores` 和 `labels` 是可迭代对象

#### 4. 维度错误

**问题**：`IndexError: boolean index did not match indexed array along dimension` 或 `ValueError: shapes (N,) and (M,) not aligned`

**解决方案**：
- 检查 `boxes` 数组的维度是否正确（应为 (N, 4)）
- 确保 `scores` 和 `labels` 的长度与 `boxes` 的长度一致
- 确保每张图片的预测结果和真实标签格式一致

#### 5. 性能问题

**问题**：评估速度太慢

**解决方案**：
- 启用并行计算：`n_jobs=-1` 或指定合适的核心数
- 只计算需要的指标：`metrics=['mAP', 'mAP_50']`
- 对于大规模数据集，考虑分批评估

#### 6. 结果异常

**问题**：评估结果为 0 或异常值

**解决方案**：
- 检查输入数据是否为空
- 检查类别索引是否正确
- 检查边界框坐标是否在正确的范围内
- 检查 IoU 计算是否正确

### 调试技巧

1. **逐步调试**：从小规模数据开始测试，确保基本功能正常

2. **数据验证**：在评估前验证输入数据的格式和内容

3. **日志输出**：使用 `print` 语句或日志记录中间结果

4. **简化测试**：创建最小复现案例，隔离问题

5. **查看源码**：如果遇到复杂问题，可以查看相关源码文件了解实现细节

## 📚 相关文档

- **指标详解.md**：详细介绍各种评估指标的计算原理
- **README.md**：项目概述和快速开始指南
- **demo.py**：完整的代码示例，覆盖所有主要功能
