# 热成像威胁检测算法

面向城市巷战无人机场景，基于温度频段分解的多类型威胁实时检测算法。

C++ 实现，用于无人机机载部署。

---

## 检测能力

输入一张红外热成像图片，输出检测到的威胁目标和整体威胁等级。

| 威胁类型 | 检测原理 | 对应场景 |
|----------|----------|----------|
| **Person** (人员) | 体温频段 30-42°C + 形状筛选 | 敌方步兵 |
| **Barrel Overheat** (枪管过热) | 热频段 >55°C + 细长形状 + 靠近人员 | 连续射击后枪管热辐射 |
| **Flash Residue** (闪光残留) | 小面积圆形 + 极高温 >90°C + 靠近人员 | 枪口火焰热残留 |
| **Cold Weapon** (冷武器) | 人员区域内 Canny 边缘 + 细长 + 宽度恒定 | 金属武器 |
| **Hotspot** (异常热点) | 高温区域 >80°C + 温差/面积/置信度过滤 | 爆炸残留、火灾 |

**威胁等级评估：**

```
SAFE → CAUTION → WARNING → DANGER → CRITICAL → SUSPECTED_FIRE

人员 + 枪管/冷武器    → CRITICAL（武装人员）
人员 + 多处闪光        → SUSPECTED_FIRE（疑似交火）
仅人员                → WARNING
仅枪管/冷武器         → DANGER / CAUTION
仅热点                → CAUTION
无检测                → SAFE
```

---

## 文件结构

```
热成像识别/
├── cpp/                          C++ 实现
│   ├── CMakeLists.txt              构建配置
│   ├── include/
│   │   ├── thermal_types.h         类型定义（威胁类型、等级、配置参数）
│   │   ├── thermal_preprocessor.h  预处理模块接口
│   │   └── thermal_detector.h      检测模块接口
│   ├── src/
│   │   ├── thermal_preprocessor.cpp  预处理实现
│   │   ├── thermal_detector.cpp      检测实现
│   │   └── main.cpp                  命令行入口
│   └── thermal_detector.exe        编译产物（Windows x64）
│
├── README.md                     本文档
│
└── test/
    └── test_images/               12 张测试热成像图片
```

---

## 算法原理

### 整体流程

```
输入热成像图片
    │
    ▼
[预处理] 灰度自动转彩色 → 色板检测 → 温度图提取 → 温度频段分解
    │
    ▼
[Pass 1] 人员检测 ──── 体温段(G通道) 阈值分割 → 形态学 → 连通域 → 形状+温度评分
    │
    ▼
[Pass 2] 热威胁检测 ── 热频段(R通道) 阈值分割 → 连通域 → 分类为 枪管/闪光/热点
    │
    ▼
[Pass 3] 冷武器检测 ── 人员区域内 Canny边缘 → 细长形状 → 宽度一致性分析
    │
    ▼
[NMS] 非极大值抑制（按类型分组去重）
    │
    ▼
[评估] 综合威胁等级 → 输出结果
```

### 步骤 1: 预处理（最关键）

预处理将各种格式的热成像图片统一转换为温度图和频段图，是整个算法的基础。

**1.1 灰度输入自动转彩色**

当输入为黑白/灰度热成像（白热或黑热模式）时，预处理器自动将其转换为 JET 彩色热图再处理，无需手动干预。灰度图 → CLAHE 对比度增强 → JET 色板映射 → 彩色图。

**1.2 色板自动检测**

热成像传感器输出格式各异，算法自动识别输入格式：

| 格式 | 判断方法 | 温度提取方式 |
|------|----------|-------------|
| 白热 (whitehot) | 灰度图，边缘暗于中心 | 像素值线性映射：T = tmin + (pixel/255) × (tmax-tmin) |
| 黑热 (blackhot) | 灰度图，边缘亮于中心 | 反转后线性映射：T = tmin + ((255-pixel)/255) × (tmax-tmin) |
| JET 彩虹 | 蓝+红通道同时显著 | 通过逆查找表从伪彩色反推灰度索引 |
| Ironbow | 红橙为主、极少纯蓝 | 逆查找表恢复 |
| 通用 RGB 热图 | 红色通道显著 | 加权组合：T = R×0.50 + G×0.30 + (1-B)×0.20，直方图拉伸 |
| 灰度 | 兜底 | 自动转彩色后按 JET 处理 |

**1.3 温度频段分解**

将温度图分解为三个独立频段（对应 BGR 三通道）：

```
R 通道（热频段）: T > 55°C    → 线性映射到 0-255
G 通道（体温段）: 30-42°C     → 高斯核突出 (中心 36°C, σ=4°C)
B 通道（环境段）: T < 20°C    → 线性映射到 0-255
```

体温段使用高斯核 `exp(-((T-36)/4)²)` 而非硬阈值，使体温附近像素获得高权重，远离体温的像素迅速衰减。

环境温度通过图像边缘 10% 区域的中位数估计。

### 步骤 2: 人员检测

在体温段（G 通道）上操作：

1. **阈值分割**: G 通道 > 40 的像素标记为候选
2. **形态学处理**: 开运算去噪 + 闭运算填充，核大小根据图像分辨率自适应（>500px 用 5×5，否则 3×3）
3. **连通域分析**: 提取所有连通域，过滤条件：
   - 面积：400 ~ 图像面积×15%
   - 长宽比：≥ 1.3（人竖直站立）
   - 方向：高度 ≥ 宽度
   - 温度：最高温 ≥ 30°C，平均温 ≤ 47°C
4. **综合评分** (加权求和):

```
confidence = aspect_ratio × 0.15
           + solidity × 0.15      (凸包面积比，人体较密实)
           + fill × 0.10          (bbox填充率)
           + temp_score × 0.20     (体温合理性 + 温度均匀性)
           + size × 0.15          (面积得分)
           + band_coverage × 0.25  (体温段像素占比)
```

最多保留 8 个人员（按置信度排序）。

### 步骤 3: 热威胁检测

在热频段（R 通道）上操作：

1. **阈值分割**: R 通道 > 80（比人员检测更严格，减少噪声）
2. **形态学处理**: 5×5 椭圆核开闭运算
3. **连通域分析**: 面积 ≥ 80px 的连通域进入分类

**分类逻辑**（按优先级）：

- **闪光残留**: 面积 < 400px 且 圆度 ≥ 0.5 且 最高温 ≥ 90°C 且 靠近人员
- **枪管过热**: 长宽比 2.5-12 且 最高温 ≥ 55°C 且 靠近人员
- **热点**: 最高温 ≥ 80°C 且 最高温 ≥ 环境温度+40°C 且 面积 80-5000px

"靠近人员"定义为：连通域中心到最近人员中心的距离 < 人员尺寸的指定倍数。

热点按置信度排序，最多保留 20 个。

### 步骤 4: 冷武器检测

在每个检测到的人员 ROI（扩展 30%）内操作：

1. **边缘检测**: Canny(edge=30, threshold=100)
2. **方向膨胀**: 分别用 5×1 和 1×5 矩形核膨胀，检测水平和垂直细长形状
3. **连通域筛选**:
   - 长宽比 2.0-10.0
   - 最短长度 ≥ 15px
   - 短边 < 人员最短边的 15%（比手臂更细）
4. **宽度一致性分析**（核心判据）:

沿长轴方向将连通域切成 N 片，统计每片宽度，计算变异系数 CV = 标准差/均值。

```
CV < 0.20 → 1.0 (恒定宽度，刚性物体如枪管)
CV < 0.35 → 0.7
CV < 0.50 → 0.3
CV ≥ 0.50 → 0.0 (宽度变化大，非刚性如手臂/布料)
```

5. **温度过滤**: 平均温度 < 38°C（排除体温区域）
6. **位置过滤**: 必须与人员 bbox 有 ≥ 5% 的重叠

### 步骤 5: NMS 和输出

按威胁类型分组，每组内按置信度降序排列，IoU > 0.35 的低置信度框被抑制。

---

## 关键参数

所有参数在 `thermal_types.h` 的 `DetectorConfig` 结构体中集中定义，可根据场景调整。

| 参数 | 默认值 | 说明 |
|------|--------|------|
| `person_min_area` | 400 | 人员最小面积 (px) |
| `person_ar_min` | 1.3 | 人员最小长宽比 |
| `person_temp_min/max` | 30 / 42 | 人员温度范围 (°C) |
| `max_persons` | 8 | 最大人数 |
| `barrel_ar_min/max` | 2.5 / 12.0 | 枪管长宽比范围 |
| `barrel_temp_min` | 55 | 枪管温度下限 (°C) |
| `barrel_proximity_ratio` | 2.0 | 枪管与人员最大距离（倍数于人员尺寸） |
| `flash_area_max` | 400 | 闪光最大面积 (px) |
| `flash_temp_min` | 90 | 闪光温度下限 (°C) |
| `hotspot_absolute_min_temp` | 80 | 热点绝对温度下限 (°C) |
| `hotspot_temp_offset` | 40 | 热点需超过环境温度的偏移 (°C) |
| `hotspot_min_area` | 80 | 热点最小面积 (px) |
| `hotspot_max_count` | 20 | 最多保留热点数 |
| `cold_weapon_ar_min/max` | 2.0 / 10.0 | 冷武器长宽比范围 |
| `cold_weapon_width_cv` | 0.35 | 冷武器宽度变异系数阈值 |
| `confidence_threshold` | 0.25 | 通用置信度下限 |
| `nms_iou_threshold` | 0.35 | NMS IoU 阈值 |

---

## 编译

依赖：OpenCV ≥ 4.5（需包含 core, imgproc, imgcodecs, highgui），CMake ≥ 3.16，C++17 编译器。

```bash
cd cpp
mkdir build && cd build

# 方式一：用 vcpkg 安装的 OpenCV
cmake .. -DCMAKE_TOOLCHAIN_FILE=<vcpkg路径>/scripts/buildsystems/vcpkg.cmake

# 方式二：用预编译 OpenCV
cmake .. -DOpenCV_DIR=<opencv路径>/build/x64/vc16/lib

# 方式三：用 conda 安装的 OpenCV
cmake .. -DCMAKE_PREFIX_PATH=<conda_env>/Library

cmake --build . --config Release
```

---

## 运行

```bash
# 分析单张图片
thermal_detector.exe image.jpg

# 分析并输出 JSON（供其他程序调用）
thermal_detector.exe image.jpg --json

# 不弹窗，只保存结果图
thermal_detector.exe image.jpg --no-show

# 查看帮助
thermal_detector.exe --help
```

**输出示例：**

```
========================================
 Image: 20260510170127_1.jpg
========================================
 Time: 427 ms
 Ambient: 24 C
 Level: [Critical] 37 detection(s)
   Person x3
   Barrel Overheat x11
   Cold Weapon x9
   Flash Residue x1
   Hotspot x13
    [Hotspot] conf:93% temp:132C area:288px
    [Person] conf:87% temp:37C area:2667px
    ...
========================================
 Saved: result_20260510170127_1.jpg
```

---

## 作为库调用

C++ 检测器可以编译为库供其他代码调用，只需包含两个头文件：

```cpp
#include "thermal_preprocessor.h"
#include "thermal_detector.h"

// 初始化（只需一次）
ThermalPreprocessor preprocessor(-20.0, 150.0);  // 温度范围
ThermalDetector detector;  // 使用默认参数

// 检测（灰度图自动转彩色，无需预处理）
cv::Mat image = cv::imread("thermal.jpg");
auto preprocessed = preprocessor.preprocess(image);
Assessment assessment = detector.detect(preprocessed);

// 获取结果
if (assessment.is_dangerous()) {
    ThreatLevel level = assessment.level();
    for (const auto& person : assessment.persons) {
        // person.bbox, person.confidence, person.max_temp ...
    }
}

// 可视化
cv::Mat vis = ThermalDetector::draw_detections(image, assessment);
cv::imwrite("result.jpg", vis);
```

### 灰度转彩色（手动调用）

```cpp
// 手动将灰度图转为 JET 彩色
cv::Mat colorized = preprocessor.colorize(gray_image, "jet", ColormapType::WHITEHOT);
```

预处理默认开启 `auto_colorize=true`，黑白热成像会自动转彩色后再检测。

---

## 移植到其他语言的要点

算法核心只使用了 OpenCV 的基础操作，不依赖任何框架特有的 API。移植时需关注：

1. **温度频段分解**: 纯数学运算（线性映射 + 高斯函数 + 阈值），任何语言都能实现
2. **连通域分析**: OpenCV 的 `connectedComponentsWithStats` 在所有语言绑定中都有
3. **形状特征**: 圆度 = 4π×面积/周长²，实心度 = 面积/凸包面积，是通用几何公式
4. **NMS**: 标准 IoU 计算和贪心抑制，约 20 行代码
5. **宽度一致性**: 沿长轴切片统计宽度，计算 CV 值，不依赖任何库

唯一需要注意的：色板自动检测部分有较多启发式规则（判断灰度图边缘/中心的亮度关系等），需要根据实际传感器输出调整判断条件。
