红外标注

lishihuan大约 10 分钟

红外标注

目前有一个可测试的demo: D:\svn\yunjian\sfpt\infrared-image-annotation

一、DJI Thermal Analysis Tool 3 功能解读

  1. 支持的素材
    • 主要解析 DJI 行业系列无人机(Mavic 2 Enterprise Advanced、M30T、M3T、XT2 等)拍摄的 R-JPEG(Radiometric JPEG)/ TIFF。
    • R-JPEG 内嵌完整辐射温度矩阵以及多组校正参数(Gain、Offset、R-B、Emissivity、距目标距离、湿度等),软件会自动读取并完成温度反算。

  2. 核心分析能力
    • 伪彩/调色板:常用 Rainbow、IronBow、White-Hot、Black-Hot… 可同步调整温标上下限(auto scale / manual)。
    • ROI 测温:
    – 点测温(Spot)——显示单点温度;
    – 线测温(Line)——给出端点温度及沿线 Profile;
    – 区域测温(Rect / Polygon)——最热、最冷、平均温度。
    • 等温线 / 填充(Isotherm):对温度阈值以上/以下区域染色高亮,用于火点、热漏检测。
    • 报告与批量:一键导出 PDF/CSV,包含截图、ROI 数据、EXIF 信息;可批量对多张图进行同一阈值分析。
    • 画面校正:镜头像素非均匀性校正(NUC)、可手动校正温漂,修正反射/环境参数。
    • 辅助工具:温度曲线随时间变化(视频)、标注注释、对照可见光。

  3. 典型使用流程

    1. 导入 R-JPEG → 软件解析辐射矩阵。
    2. 选择调色板&温标 → 直观查看热分布。
    3. 布置 ROI(点/线/区)→ 得到实时温度值。
    4. 设置等温线阈值 → 快速锁定异常区域。
    5. 导出报告或 CSV 供后续工程/安监使用。

二、自己实现类似功能的思路

A. 关键技术挑战

  1. 解析 DJI R-JPEG/TIFF,并把“每个像素 → 温度”矩阵解码出来;
  2. 可视化:高效渲染大尺寸温度矩阵,支持调色板映射、阈值渲染;
  3. ROI 与交互:鼠标拖拽/点击生成点、线、区域并计算统计信息;
  4. 参数校正:根据 EXIF 里的 Gain、Offset、环境温度、发射率等做温度修正(可做简化版先不支持);
  5. 报告/数据导出:生成截图、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 为例)

  1. 读文件
    – 通过 <input type="file"> 选择 R-JPEG;
    – 使用 exif-reader 解析 DJI Thermal MakerNote 中的字段(如 ThermalDataGain, Offset)。
    – 将 16-bit Raw 温度数组读成 Uint16Array

  2. 数据→温度
    – 若 metadata 给出的即是 单位 0.1°C,直接 temp = raw / 10;
    – 若仅给出 RawRadiometric 值,按 T = Gain / (raw + Offset) 或厂家公式换算。

  3. 画布渲染
    – 把温度矩阵存到 Float32Array;
    – 根据当前 palette & min/max,把温度映射到 RGB:

    const idx = (temp - tMin) / (tMax - tMin);
    const color = palette[Math.floor(idx * (palette.length - 1))];
    

    – 用 ImageData 或 WebGL texture2D 一次写入 Canvas;WebGL 性能更优。

  4. ROI 测温
    – 设计 rois = [],类型: point / line / rect / poly;
    – 鼠标按下记录起点,松开记录终点或多边形点集;
    – 渲染时在 Canvas 叠一层 <svg> 或第 2 个 canvas 画 ROI 边框;
    – 计算:
    • 点:直接 temp = matrix[y * width + x]
    • 线:用 Bresenham 采样所有像素、取 min/max/avg;
    • 区域:取包围盒内像素并按多边形掩膜过滤。

  5. 等温线/阈值高亮
    – 设定阈值 Tiso,重绘时若 temp >= Tiso,用指定颜色(或 alpha 蒙层)覆盖。

  6. 参数面板
    – 左侧栏:调色板下拉、温度滑块、阈值输入、ROI 列表;
    – 每滑动/修改参数触发重新渲染即可。

  7. 报告导出
    – 使用 canvas.toDataURL() 导出带 ROI 的图;
    – 将 ROI 温度统计组装成 JSON ∕ CSV;
    – 可调用 jsPDF 生成 PDF。

D. 可迭代路线

  1. 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)读数差距巨大,是因为我为了快速演示,采用了“灰度≈温度”的极简占位算法:

  1. 取每个像素的 R/G/B 做平均值 avg = (R+G+B)/3;
  2. 把 avg 直接当作 0-255 范围内的“摄氏度”。

这只是为了让伪彩条 & ROI 逻辑跑得起来,并不是真正的辐射温度。
而 TA3 读取的是真实辐射矩阵(16 bit 或 32 bit Raw),再结合镜头、环境、发射率等参数做校正,精度可达 ±2 ℃。

───────────────────────
如何拿到“真温度”
───────────────────────
DJI 行业机在 R-JPEG 里保存了完整的温度矩阵,流程大致如下(不同机型名字略有差异):

  1. 读取 EXIF / MakerNote
    • 关键字段:
    RawThermalImage(或 ThermalData, FLIR Image):zlib 压缩的 16-bit 温度矩阵;
    Gain, Offset:将 Raw 计数还原为绝对温度;
    Emissivity, ReflectTemp, ObjectDistance… 用于高级校正。

  2. 解压 ThermalData
    • 解码后得到 Uint16Array,通常单位为 0.1 ℃(即值 *0.1 → 摄氏度)。
    • 个别机型需公式:

    Temp(℃) = Gain / (Raw + Offset)  –  273.15
    
  3. 温度→伪彩
    • 用上述真实温度矩阵替换掉代码里的 temperatureMatrix
    • 其他调色、ROI 统计逻辑保持不变,读数即与 TA3 基本一致。

───────────────────────
在浏览器端可行的技术路径
───────────────────────

  1. 读取 EXIF
    • 引入 exifrpiexifjs

    <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
    
  2. 解压 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);
    
  3. 转成摄氏度

    const gain   = meta.Gain || 0.04;   // 示例值,需读真参数
    const offset = meta.Offset || 1000; // 示例值
    const temps = Float32Array.from(raw16, v => v * 0.1); // 或用公式
    
  4. 替换现有 temperatureMatrix

    this.temperatureMatrix = temps;
    this.imgWidth  = meta.ImageWidth;
    this.imgHeight = meta.ImageHeight;
    
  5. 可选:发射率与环境修正

    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 做什么?
───────────────────────

  1. 集成 exifr + pako,真正解析 DJI R-JPEG;
  2. 增加参数面板:发射率、反射温度、距离输入并实时校正;
  3. 提供 “导入 JPG / TIFF” 与 “导入普通图” 两种入口;
  4. 性能优化:
    • 大图 (>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 / OffsetK2 / B原始值 ➜ 温度计算参数
Emissivity发射率,影响真实物体温度计算
ReflectedApparentTemperature反射背景温度修正
RawThermalImageWidth / Height温度矩阵的真实尺寸

✅ 如果你能从 EXIF/MakerNote 中成功提取到这些数据(比如通过 exifropen in new window + pakoopen in new window),那你就能在前端直接实现 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-JPEGRawThermalImage,有 Gain/Offset✅ 支持温度计算
⚠️ 部分兼容RawThermalImage 但有 K2/B⚠️ 需验证后尝试
❌ 普通图没有任何温度字段❌ 无法测温

五、具体实现

目前计划采用的技术栈:vue2+Paper.js

前端上传 R-JPEG / R-TIFF 图像 ➜ 后端用 DJI Thermal SDK 提取温度矩阵 ➜ 返回给前端 ➜ 前端基于这个矩阵做打点、绘制、统计温度等操作。