红外标注
红外标注
目前有一个可测试的demo: D:\svn\yunjian\sfpt\infrared-image-annotation
一、DJI Thermal Analysis Tool 3 功能解读
支持的素材
• 主要解析 DJI 行业系列无人机(Mavic 2 Enterprise Advanced、M30T、M3T、XT2 等)拍摄的 R-JPEG(Radiometric JPEG)/ TIFF。
• R-JPEG 内嵌完整辐射温度矩阵以及多组校正参数(Gain、Offset、R-B、Emissivity、距目标距离、湿度等),软件会自动读取并完成温度反算。核心分析能力
• 伪彩/调色板:常用 Rainbow、IronBow、White-Hot、Black-Hot… 可同步调整温标上下限(auto scale / manual)。
• ROI 测温:
– 点测温(Spot)——显示单点温度;
– 线测温(Line)——给出端点温度及沿线 Profile;
– 区域测温(Rect / Polygon)——最热、最冷、平均温度。
• 等温线 / 填充(Isotherm):对温度阈值以上/以下区域染色高亮,用于火点、热漏检测。
• 报告与批量:一键导出 PDF/CSV,包含截图、ROI 数据、EXIF 信息;可批量对多张图进行同一阈值分析。
• 画面校正:镜头像素非均匀性校正(NUC)、可手动校正温漂,修正反射/环境参数。
• 辅助工具:温度曲线随时间变化(视频)、标注注释、对照可见光。典型使用流程
- 导入 R-JPEG → 软件解析辐射矩阵。
- 选择调色板&温标 → 直观查看热分布。
- 布置 ROI(点/线/区)→ 得到实时温度值。
- 设置等温线阈值 → 快速锁定异常区域。
- 导出报告或 CSV 供后续工程/安监使用。
二、自己实现类似功能的思路
A. 关键技术挑战
- 解析 DJI R-JPEG/TIFF,并把“每个像素 → 温度”矩阵解码出来;
- 可视化:高效渲染大尺寸温度矩阵,支持调色板映射、阈值渲染;
- ROI 与交互:鼠标拖拽/点击生成点、线、区域并计算统计信息;
- 参数校正:根据 EXIF 里的 Gain、Offset、环境温度、发射率等做温度修正(可做简化版先不支持);
- 报告/数据导出:生成截图、CSV 或 JSON。
B. 推荐技术栈
• Web 端:Vue3 + Canvas/WebGL + exif-reader(JS)+ wasm-版 OpenCV 或 rumble-charts 等;
• 桌面端:Python(PySide6/Qt6)+ OpenCV + flirpy/DJI SDK;
• 数据解析:
– JS 端可用 exif-js 读取 R-JPEG 中的 MakerNotes;
– Python 端可用 pillow + exifread 或直接调用 exiftool;
– 若是 H20T/M30T,温度值通常已存为 16-bit little-endian 数组,单位 0.1°C,可直接读取无需复杂辐射校正。
C. 基本实现步骤(以前端 Vue3 为例)
读文件
– 通过<input type="file">选择 R-JPEG;
– 使用 exif-reader 解析DJI ThermalMakerNote 中的字段(如ThermalData、Gain,Offset)。
– 将 16-bit Raw 温度数组读成Uint16Array。数据→温度
– 若 metadata 给出的即是 单位 0.1°C,直接 temp = raw / 10;
– 若仅给出 RawRadiometric 值,按T = Gain / (raw + Offset)或厂家公式换算。画布渲染
– 把温度矩阵存到 Float32Array;
– 根据当前 palette & min/max,把温度映射到 RGB:const idx = (temp - tMin) / (tMax - tMin); const color = palette[Math.floor(idx * (palette.length - 1))];– 用
ImageData或 WebGLtexture2D一次写入 Canvas;WebGL 性能更优。ROI 测温
– 设计rois = [],类型: point / line / rect / poly;
– 鼠标按下记录起点,松开记录终点或多边形点集;
– 渲染时在 Canvas 叠一层<svg>或第 2 个 canvas 画 ROI 边框;
– 计算:
• 点:直接temp = matrix[y * width + x];
• 线:用 Bresenham 采样所有像素、取 min/max/avg;
• 区域:取包围盒内像素并按多边形掩膜过滤。等温线/阈值高亮
– 设定阈值Tiso,重绘时若temp >= Tiso,用指定颜色(或 alpha 蒙层)覆盖。参数面板
– 左侧栏:调色板下拉、温度滑块、阈值输入、ROI 列表;
– 每滑动/修改参数触发重新渲染即可。报告导出
– 使用canvas.toDataURL()导出带 ROI 的图;
– 将 ROI 温度统计组装成 JSON ∕ CSV;
– 可调用 jsPDF 生成 PDF。
D. 可迭代路线
- MVP:
• 解析 R-JPEG → Canvas 伪彩 → 单点/矩形 ROI 实时温度。 - Isotherm + 报告导出;
- 线 ROI、折线 Profile;
- 参数校正面板(发射率、环境温度)→ 温度精度提升;
- 批量处理 & 视频序列(逐帧分析)。
──────────────────
三、小结
• TA3 的核心是“正确读取温度 + 丰富交互分析 + 报告输出”。
• 解析 DJI R-JPEG 相对简单,多数型号已直接存温度值;你只需把温度矩阵映射成彩色图,并围绕它构建 ROI 与阈值逻辑。
• 前端栈里 Canvas/WebGL 足够处理 640×512、1280×1024 级别分辨率;若追求性能,可将渲染和温度计算放到 WebGL shader 或 wasm。
• 推荐先完成 MVP 再逐步添加 TA3 的高级特性。
有了以上思路,你就可以在现有的 Vue 代码基础上,从“固定 RGB→温度”转换,升级为“读取 R-JPEG 真温度 → 渲染 → ROI 测温”,逐步逼近 DJI TA3 的体验。祝开发顺利!
四、关于温度
当前 Demo 之所以与 DJI Thermal Analysis Tool 3(以下简称 TA3)读数差距巨大,是因为我为了快速演示,采用了“灰度≈温度”的极简占位算法:
- 取每个像素的 R/G/B 做平均值 avg = (R+G+B)/3;
- 把 avg 直接当作 0-255 范围内的“摄氏度”。
这只是为了让伪彩条 & ROI 逻辑跑得起来,并不是真正的辐射温度。
而 TA3 读取的是真实辐射矩阵(16 bit 或 32 bit Raw),再结合镜头、环境、发射率等参数做校正,精度可达 ±2 ℃。
───────────────────────
如何拿到“真温度”
───────────────────────
DJI 行业机在 R-JPEG 里保存了完整的温度矩阵,流程大致如下(不同机型名字略有差异):
读取 EXIF / MakerNote
• 关键字段:
–RawThermalImage(或ThermalData,FLIR Image):zlib 压缩的 16-bit 温度矩阵;
–Gain,Offset:将 Raw 计数还原为绝对温度;
–Emissivity,ReflectTemp,ObjectDistance… 用于高级校正。解压 ThermalData
• 解码后得到Uint16Array,通常单位为 0.1 ℃(即值 *0.1 → 摄氏度)。
• 个别机型需公式:Temp(℃) = Gain / (Raw + Offset) – 273.15温度→伪彩
• 用上述真实温度矩阵替换掉代码里的temperatureMatrix。
• 其他调色、ROI 统计逻辑保持不变,读数即与 TA3 基本一致。
───────────────────────
在浏览器端可行的技术路径
───────────────────────
读取 EXIF
• 引入exifr或piexifjs:<script src="https://cdn.jsdelivr.net/npm/exifr/dist/lite.umd.js"></script>• 示例:
const meta = await exifr.parse(file, { tiff: true, xmp: true, strip: false }); const compressed = meta.RawThermalImage; // ArrayBuffer解压 ThermalData
• 浏览器端可用pako(zlib):import pako from 'https://cdn.jsdelivr.net/npm/pako@2.1.0/+esm' const raw16 = new Uint16Array(pako.inflate(new Uint8Array(compressed)).buffer);转成摄氏度
const gain = meta.Gain || 0.04; // 示例值,需读真参数 const offset = meta.Offset || 1000; // 示例值 const temps = Float32Array.from(raw16, v => v * 0.1); // 或用公式替换现有
temperatureMatrixthis.temperatureMatrix = temps; this.imgWidth = meta.ImageWidth; this.imgHeight = meta.ImageHeight;可选:发射率与环境修正
Tcorr = (Traw - (1 - ε) * (Tamb^4 - Tref^4)) ^0.25
───────────────────────
为何我建议“浏览器 + 离屏 Canvas + Paper.js”
───────────────────────
• zlib 解压 + 60 万像素温度矩阵 JS 端仍能跑;若担心性能可用 WebAssembly 实现。
• 全在前端完成,文件不离开浏览器,满足隐私/网速要求。
• Paper.js + 单画布方案已支持缩放/平移:
paper.view.scale(1.2);
paper.view.translate( dx, dy );
底图 Raster 与 ROI 会一起变换。
───────────────────────
下一步我可以为 Demo 做什么?
───────────────────────
- 集成
exifr+pako,真正解析 DJI R-JPEG; - 增加参数面板:发射率、反射温度、距离输入并实时校正;
- 提供 “导入 JPG / TIFF” 与 “导入普通图” 两种入口;
- 性能优化:
• 大图 (>1280×1024) 时用 WebGL shader 进行伪彩;
• 只在参数改变时重算像素,ROI 绘制不受影响。
如需我动手将解析逻辑写进当前项目,请告诉我;也可先确定目标机型/文件样例,方便对症开发。
技术方案
A) 后端解析,前端展示
• 用 Thermal SDK(或其 Python 封装)在服务器把 R-JPEG → JSON ⟶ 温度矩阵。
• Vue2 + Paper.js 只负责调色板映射、ROI 交互。
优点:浏览器端不跑解压/校正,性能好;缺点:要传文件到后端。
B) WebAssembly 封装
• 把 Thermal SDK 的 C 接口用 Emscripten 编译成 wasm,在浏览器直接解析 R-JPEG。
• 好处是文件不出本地;但编译过程和授权条款需确认可行。
C) 继续用 exifr + 自研公式
• 如果机型固定,靠读取 Gain/Offset/K2/B 已经能做到 ±2 ℃ 内,可能足够。
• 仅需前端,无额外授权。
「R-JPEG / R-TIFF 是什么」以及「是否能直接在前端实现 ROI 测温」
✅ 一句话解释:
R-JPEG / R-TIFF 是一种嵌入了红外辐射温度原始数据的图像格式,不像普通图片只有 RGB 可视图,它们内部还携带了每个像素的红外温度值(或可转换为温度的原始数据),因此可以实现像 DJI Thermal Analysis Tool 这样的测温工具。
🔍 详细说明
| 格式类型 | 说明 | 是否能前端测温 |
|---|---|---|
| 普通 JPEG / PNG | 只有图像像素(RGB),无法还原温度 | ❌ 无法测温 |
| R-JPEG / R-TIFF | 携带了红外原始数据(16-bit 原始值、发射率、反射温度等) | ✅ 可以测温(如用 exifr + pako 解码) |
| DJI 专用格式 | DJI 的 R-JPEG 是在 JPEG 中嵌入 MakerNote 或 XMP 方式存储 RawThermalImage 和参数 | ✅ 可前端测温,需解析 MakerNote + 解压 |
📦 R-JPEG 中通常包含:
| 数据项 | 说明 |
|---|---|
RawThermalImage | 一段压缩的 zlib 数据,解压后是每像素的 16bit 原始红外值(uint16) |
Gain / Offset 或 K2 / B | 原始值 ➜ 温度计算参数 |
Emissivity | 发射率,影响真实物体温度计算 |
ReflectedApparentTemperature | 反射背景温度修正 |
RawThermalImageWidth / Height | 温度矩阵的真实尺寸 |
✅ 如果你能从 EXIF/MakerNote 中成功提取到这些数据(比如通过 exifr + pako),那你就能在前端直接实现 ROI 区域测温功能。
🎯 总结:是否能「前端直接实现 ROI 测温」?
| 条件 | 能否测温 |
|---|---|
| 📷 普通 RGB 图像(无红外数据) | ❌ 不行 |
| ✅ R-JPEG / R-TIFF 并且前端能提取 RawThermalImage + 参数 | ✅ 可以直接在前端实现测温 |
| ❓ 图片有红外信息但格式非公开或 SDK 私有 | ❌ 前端不行,需后端 SDK 支持(比如 DJI Thermal SDK) |
✳️ 补充:是否所有 DJI 设备都支持 R-JPEG?
不是。以下是常见情况:
| 设备 | 是否支持 R-JPEG |
|---|---|
| DJI M3T、H20T、XT2 等热成像相机 | ✅ 支持(需正确配置拍摄设置) |
| DJI 普通可见光相机 | ❌ 不支持 |
| 热成像图为 IronRed 等但未嵌入原始数据 | ❌ 无法测温(仅渲染后的伪彩图) |
你可以通过查看 exifr 输出中是否含有 RawThermalImage 字段来判断。
前端判断是否为 R-JPEG 的函数
<input type="file" accept=".jpg,.jpeg,.tif,.tiff" onchange="handleFile(this.files[0])" />
<script>
async function handleFile(file) {
const isRJpeg = await isRadiometricImage(file);
if (isRJpeg) {
alert('✅ 是带红外信息的 R-JPEG / R-TIFF');
} else {
alert('❌ 不是带红外信息的图片,无法测温');
}
}
async function isRadiometricImage(file) {
try {
const exif = await exifr.parse(file, {
mergeTags: false,
tiff: true,
xmp: true
});
if (!exif) return false;
// 1. 关键字段:RawThermalImage 直接包含原始红外数据(zlib 压缩)
if (exif.RawThermalImage) return true;
// 2. 次要字段:判断是否包含辐射测温相关参数(但不绝对可靠)
const hasTemperatureParams = (
'Gain' in exif || 'Offset' in exif ||
'K2' in exif || 'B' in exif
);
const hasSizeInfo = (
'RawThermalImageWidth' in exif ||
'RawThermalImageHeight' in exif ||
('PixelXDimension' in exif && 'PixelYDimension' in exif)
);
return hasTemperatureParams && hasSizeInfo;
} catch (err) {
console.error('EXIF 解析失败:', err);
return false;
}
}
</script>
| 类型 | 示例返回内容 | 是否可测温 |
|---|---|---|
| ✅ R-JPEG | 有 RawThermalImage,有 Gain/Offset | ✅ 支持温度计算 |
| ⚠️ 部分兼容 | 无 RawThermalImage 但有 K2/B 等 | ⚠️ 需验证后尝试 |
| ❌ 普通图 | 没有任何温度字段 | ❌ 无法测温 |
五、具体实现
目前计划采用的技术栈:vue2+Paper.js
前端上传 R-JPEG / R-TIFF 图像 ➜ 后端用 DJI Thermal SDK 提取温度矩阵 ➜ 返回给前端 ➜ 前端基于这个矩阵做打点、绘制、统计温度等操作。