ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python直控FLIR红外相机获取辐射值与温度图

Python直控FLIR红外相机获取辐射值与温度图 简介本资源是一套面向Python开发者与红外图像处理工程师的FLIR红外相机SDK调用实践方案聚焦温度数据采集、热成像捕获、调色板渲染及图像后处理等核心任务适用于工业测温监控、科研热分析、自动化检测等实际场景。压缩包共421个文件涵盖288个Python源码含相机初始化、图像捕获、温度解析、调色板应用等完整流程脚本、77个DLL与22个PYD动态库提供底层硬件通信与图像格式转换支持、11个EXE可执行工具及配置类文件如bat启动脚本、cfg参数模板、pth路径配置整体体积31.9MB结构清晰开箱即用。已有2080人学习下载资源包含可直接运行的示例代码、多版本兼容的SDK依赖库及典型红外图像处理链路从原始16位温度矩阵到RGB可视化输出并内置OpenCV适配模块与IRSDK格式插件显著降低环境配置门槛与集成难度。1. 用 Python 直接驱动 FLIR 红外相机不是调 API 而是控硬件绕过 Web UI 和厂商上位机实现温度帧流实时采集、ROI 温度统计与原始辐射值导出FLIR 红外相机如 A655sc、AX5、Boson、Lepton 系列在工业测温、科研热分析、电力巡检和嵌入式视觉中广泛部署但多数用户卡在「只能用 FLIR Tools 打开录像」或「依赖 Windows 上位机导出 CSV」的阶段。实际上FLIR 官方提供完整 C/C SDK即 FLIR Research IR SDK支持 Linux/Windows/macOS而 Python 并非“不支持”——而是需通过 ctypes 或 pybind11 封装原生库直接访问相机底层帧缓冲区、辐射校准参数与非均匀性校正NUC状态。本文聚焦真实产线与实验室场景不依赖 GUI不走 HTTP 接口不调用已封装的第三方 wrapper如 flirpy 已停更且不支持 AX5/Boson而是从零构建可复现、可调试、可集成进 OpenCV 流程的 Python 控制链。适合需要做实时温度分布图、多 ROI 动态阈值报警、辐射数据标定或与 FPGA/PLC 同步触发的工程师——尤其当你发现flirpy报错No device found或Failed to initialize context时本方案就是底层替代路径。2. 为什么必须用 FLIR Research IR SDK 而非通用 V4L2 或 OpenCV VideoCapture2.1 FLIR 相机的硬件通信本质USB3 Vision GenICam 协议栈非标准 UVC 设备FLIR 中高端红外相机Axx、AXx、Boson、Tau2默认工作在 USB3 Vision 模式其底层协议基于 GenICam 标准而非普通摄像头的 UVCUSB Video Class。这意味着cv2.VideoCapture(0)无法打开——OpenCV 的 UVC 后端不解析 GenICam XML 描述符ls /dev/video*列不出设备节点——USB3 Vision 使用专用内核模块如uvcvideo不加载需usbvision或genicam驱动v4l2-ctl --list-devices返回空——V4L2 接口未暴露控制通道。提示可通过lsusb -v | grep -A 10 FLIR验证设备描述符中是否含bInterfaceClass 0xffVendor Specific这是 USB3 Vision 的典型标识若显示bInterfaceClass 0x0eVideo则为 UVC 模式仅限部分 Lepton 或旧款入门机型此时可用 OpenCV但丢失辐射校准能力。2.2 Research IR SDK 的不可替代性辐射值Radiometric Data≠ 伪彩色图像FLIR 相机输出两类数据流Display Stream8-bit YUV/RGB经 gamma、伪彩色 LUT、对比度增强处理供人眼观察Radiometric Stream16-bit raw sensor data单位DN经 NUC、PRNU、温度校准模型Planck 方程 相机固有参数转换为物理辐射值单位W/m²·sr·nm再反演为物体表面温度℃。Research IR SDK 唯一提供GetRadiometricImage()和GetTemperatureImage()接口返回带标定元数据的 numpy 数组。而任何基于cv2.imread()或PIL.Image.open()读取的 PNG/JPEG 图像均为 Display Stream无法用于定量测温——这是工业现场误报率高的根本原因。2.3 SDK 版本与平台兼容性决策树相机型号推荐 SDK 版本支持平台Python 绑定方式关键限制A655sc, A35Research IR 3.1Windows x64, Linux x64ctypes官方示例Linux 需 udev 规则 用户组权限Boson, Tau2Research IR 4.0Windows x64, Linux x64pybind11社区维护4.0 移除对 .NET Framework 依赖Lepton 3.5Lepton SDK 2.0Windows x64ctypes非 Research IR仅支持 radiometric mode无 GenICam 控制注意FLIR 官网下载页search FLIR Research IR SDK提供flir_research_ir_sdk_*.zip解压后含lib/动态库、include/头文件、samples/C 示例。不要下载 FLIR Spinnaker SDK——那是用于 Point Grey 相机的与 FLIR 红外无关。3. 在 Linux Ubuntu 22.04 上用 ctypes 封装 Research IR SDK 实现最小可运行流程3.1 环境准备安装依赖、配置 udev 规则、验证 SDK 可加载# 安装基础编译工具与 Python 开发头文件 sudo apt update sudo apt install -y build-essential python3-dev python3-pip # 创建 udev 规则使普通用户可访问 USB 设备避免 sudo 运行 echo SUBSYSTEMusb, ATTRS{idVendor}1e10, ATTRS{idProduct}2001, MODE0666, GROUPplugdev | sudo tee /etc/udev/rules.d/99-flir.rules sudo udevadm control --reload-rules sudo usermod -a -G plugdev $USER # 重新插拔相机或执行sudo udevadm trigger # 下载并解压 Research IR SDK以 3.1.0 为例 wget https://www.flir.com/products/research-ir-sdk/ # 手动下载 ZIP unzip flir_research_ir_sdk_3.1.0_linux_x64.zip -d ~/flir-sdk export FLIR_SDK_PATH$HOME/flir-sdk3.2 ctypes 封装核心函数加载库、初始化上下文、枚举设备、打开流# flir_wrapper.py import ctypes import numpy as np import os from typing import Optional, Tuple # 加载动态库Linux: libflir_research_ir.so, Windows: flir_research_ir.dll sdk_path os.environ.get(FLIR_SDK_PATH, ) lib_path os.path.join(sdk_path, lib, libflir_research_ir.so) flir_lib ctypes.CDLL(lib_path) # 定义 C 结构体对应 SDK 头文件中的 FLIR_CameraContext、FLIR_StreamHandle 等 class FLIR_CameraContext(ctypes.Structure): _fields_ [(handle, ctypes.c_void_p)] class FLIR_StreamHandle(ctypes.Structure): _fields_ [(handle, ctypes.c_void_p)] # 函数签名声明关键必须与 SDK 头文件一致 flir_lib.FLIR_CreateContext.argtypes [ctypes.POINTER(FLIR_CameraContext)] flir_lib.FLIR_CreateContext.restype ctypes.c_int flir_lib.FLIR_DestroyContext.argtypes [ctypes.POINTER(FLIR_CameraContext)] flir_lib.FLIR_DestroyContext.restype ctypes.c_int flir_lib.FLIR_EnumerateCameras.argtypes [ ctypes.POINTER(FLIR_CameraContext), ctypes.POINTER(ctypes.c_uint32), ctypes.c_uint32 ] flir_lib.FLIR_EnumerateCameras.restype ctypes.c_int flir_lib.FLIR_OpenStream.argtypes [ ctypes.POINTER(FLIR_CameraContext), ctypes.c_uint32, ctypes.POINTER(FLIR_StreamHandle) ] flir_lib.FLIR_OpenStream.restype ctypes.c_int # 初始化上下文 ctx FLIR_CameraContext() ret flir_lib.FLIR_CreateContext(ctypes.byref(ctx)) if ret ! 0: raise RuntimeError(fFLIR_CreateContext failed with code {ret}) # 枚举设备数量 num_cameras ctypes.c_uint32(0) ret flir_lib.FLIR_EnumerateCameras(ctypes.byref(ctx), ctypes.byref(num_cameras), 0) if ret ! 0 or num_cameras.value 0: raise RuntimeError(No FLIR camera detected) print(fFound {num_cameras.value} FLIR camera(s))参数说明FLIR_CreateContext()创建全局上下文是所有后续调用的前提失败码0表示成功非零为错误如-1内存不足-2驱动未加载FLIR_EnumerateCameras()第三个参数为flags0表示枚举本地 USB 设备1表示枚举 GigE 设备需额外配置 IPctypes.byref()是传递指针的关键Python 中必须显式声明否则 C 函数无法写回值。3.3 获取辐射图像帧调用 GetRadiometricImage 并转为 numpy 数组# 继续 flir_wrapper.py flir_lib.FLIR_GetRadiometricImage.argtypes [ ctypes.POINTER(FLIR_StreamHandle), ctypes.POINTER(ctypes.c_uint16), ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32) ] flir_lib.FLIR_GetRadiometricImage.restype ctypes.c_int # 打开第一个相机流 stream FLIR_StreamHandle() ret flir_lib.FLIR_OpenStream(ctypes.byref(ctx), 0, ctypes.byref(stream)) if ret ! 0: raise RuntimeError(fFLIR_OpenStream failed: {ret}) # 分配缓冲区假设分辨率为 640x48016-bit width, height 640, 480 buffer_size width * height * 2 # uint16 2 bytes img_buffer (ctypes.c_uint16 * (width * height))() # 获取一帧辐射数据单位DN frame_count ctypes.c_uint32(0) ret flir_lib.FLIR_GetRadiometricImage( ctypes.byref(stream), img_buffer, buffer_size, ctypes.byref(frame_count) ) if ret ! 0: raise RuntimeError(fFLIR_GetRadiometricImage failed: {ret}) # 转为 numpy 数组并 reshape radiometric_img np.ctypeslib.as_array(img_buffer, shape(height, width)) print(fRadiometric frame shape: {radiometric_img.shape}, dtype: {radiometric_img.dtype}) print(fMin DN: {radiometric_img.min()}, Max DN: {radiometric_img.max()})关键逻辑说明FLIR_GetRadiometricImage()返回的是原始 DN 值Digital Number非温度值温度需用 SDK 提供的FLIR_ConvertRadiometricToTemperature()计算img_buffer必须是ctypes.c_uint16数组长度为width * height不能用np.array().ctypes.data_as()直接传——ctypes 要求连续内存块且类型严格匹配frame_count输出实际获取的帧序号可用于丢弃首帧NUC 刚完成时数据不稳定。4. 从 DN 值到物理温度调用 SDK 内置温度转换函数并验证精度4.1 调用 FLIR_ConvertRadiometricToTemperature 获取摄氏度矩阵# flir_wrapper.py 续 flir_lib.FLIR_ConvertRadiometricToTemperature.argtypes [ ctypes.POINTER(FLIR_StreamHandle), ctypes.POINTER(ctypes.c_uint16), ctypes.POINTER(ctypes.c_float), ctypes.c_uint32, ctypes.c_float, # object_emissivity ctypes.c_float # ambient_temperature_celsius ] flir_lib.FLIR_ConvertRadiometricToTemperature.restype ctypes.c_int # 分配 float32 缓冲区存储温度 temp_buffer (ctypes.c_float * (width * height))() # 设置标定参数典型值金属 emissivity0.92环境温度 25°C emissivity 0.92 ambient_temp 25.0 ret flir_lib.FLIR_ConvertRadiometricToTemperature( ctypes.byref(stream), img_buffer, temp_buffer, buffer_size, ctypes.c_float(emissivity), ctypes.c_float(ambient_temp) ) if ret ! 0: raise RuntimeError(fFLIR_ConvertRadiometricToTemperature failed: {ret}) # 转为 numpy temp_img np.ctypeslib.as_array(temp_buffer, shape(height, width)) print(fTemperature frame range: {temp_img.min():.1f}°C ~ {temp_img.max():.1f}°C)参数说明object_emissivity目标物体发射率0.0~1.0常见材料参考值铜抛光0.03铝氧化0.4人体皮肤 0.98混凝土 0.94ambient_temperature_celsius相机周围环境温度非被测物温度影响反射辐射项计算此函数内部调用 Planck 黑体辐射方程并注入相机出厂标定的 FPA 响应曲线、镜头透过率、大气衰减模型若启用大气校正。4.2 ROI 温度统计与动态报警提取区域均值、标准差、极值# 定义 ROI 区域左上角 x,y宽高 roi_x, roi_y, roi_w, roi_h 100, 100, 200, 150 # 切片 numpy 数组注意numpy 索引是 [y:yh, x:xw] roi_temp temp_img[roi_y:roi_yroi_h, roi_x:roi_xroi_w] # 计算统计量 roi_mean np.mean(roi_temp) roi_std np.std(roi_temp) roi_min, roi_max np.min(roi_temp), np.max(roi_temp) print(fROI ({roi_x},{roi_y},{roi_w},{roi_h}): fMean{roi_mean:.1f}°C, Std{roi_std:.2f}°C, fRange{roi_min:.1f}~{roi_max:.1f}°C) # 动态报警温度超过阈值且标准差 2°C排除火焰等高梯度干扰 if roi_mean 80.0 and roi_std 2.0: print(ALERT: Overheat detected in ROI!)实战技巧ROI 应避开镜头边缘存在 vignetting 暗角和高温热源边缘存在 spatial aliasingroi_std 2.0是典型稳态目标判据若检测电机轴承可设为 1.0若检测电路板焊点可放宽至 3.0实际部署时建议用cv2.rectangle()在伪彩色图上绘制 ROI 框并用cv2.putText()叠加温度文本。4.3 辐射值W/m²·sr·nm导出调用 GetRadianceImage 获取物理辐射通量# flir_lib.FLIR_GetRadianceImage 用于获取辐射亮度Radiance flir_lib.FLIR_GetRadianceImage.argtypes [ ctypes.POINTER(FLIR_StreamHandle), ctypes.POINTER(ctypes.c_float), ctypes.c_uint32 ] flir_lib.FLIR_GetRadianceImage.restype ctypes.c_int radiance_buffer (ctypes.c_float * (width * height))() ret flir_lib.FLIR_GetRadianceImage( ctypes.byref(stream), radiance_buffer, buffer_size ) if ret ! 0: raise RuntimeError(fFLIR_GetRadianceImage failed: {ret}) radiance_img np.ctypeslib.as_array(radiance_buffer, shape(height, width)) print(fRadiance range: {radiance_img.min():.2e} ~ {radiance_img.max():.2e} W/m²·sr·nm)提示辐射值是绝对物理量不受 emissivity 和 ambient temperature 影响适用于多光谱比色测温、大气透射率反演等高级应用但需确保相机已执行最新 NUC非均匀性校正否则存在固定模式噪声FPN。5. 排查常见错误段错误、设备未找到、帧超时与权限问题诊断表错误现象可能原因诊断命令解决方案Segmentation fault (core dumped)ctypes 函数参数类型/数量不匹配缓冲区大小不足gdb python -ex run -ex bt --args python test.py检查.argtypes是否与 SDK 头文件完全一致确认buffer_size为width*height*sizeof(dtype)FLIR_EnumerateCameras returns 0 devicesudev 规则未生效用户未加入plugdev组ls -l /dev/bus/usb/*/* | grep 1e10groups执行sudo udevadm trigger注销重登录检查idVendor是否为1e10FLIRFLIR_GetRadiometricImage timeout (-3)相机未触发帧采集流未启动flir_lib.FLIR_StartStream(ctypes.byref(stream))在GetRadiometricImage前调用FLIR_StartStream()确认相机 LED 为绿色非红色故障态ImportError: libflir_research_ir.so: cannot open shared object fileLD_LIBRARY_PATH 未包含 SDK lib 路径echo $LD_LIBRARY_PATHldd ./test.py | grep flirexport LD_LIBRARY_PATH$FLIR_SDK_PATH/lib:$LD_LIBRARY_PATH或复制 so 文件到/usr/libFLIR_CreateContext returns -2内核驱动未加载lsmod | grep -i flirdmesg | tail -20确保已安装flir-research-ir-dkmsUbuntu 官方包或手动编译内核模块深度排错技巧启用 SDK 日志在调用FLIR_CreateContext()前设置环境变量export FLIR_LOG_LEVEL3日志输出到/tmp/flir_sdk.log可看到 USB 设备握手细节验证相机固件版本调用FLIR_GetCameraInfo()获取firmware_version字符串低于 v2.12 的 AX5 固件存在 radiometric stream 丢帧 bug需升级绕过 NUC 等待若首帧GetRadiometricImage返回-5NUC in progress可循环调用FLIR_GetNUCStatus()直到返回0NUC completed再取帧。6. 进阶技巧将 FLIR 流集成进 OpenCV pipeline 实现伪彩色渲染与实时叠加6.1 用 OpenCV colormaps 渲染温度图并叠加 ROI 文本import cv2 # 将温度图归一化到 0-255适配 colormap 输入 temp_normalized cv2.normalize(temp_img, None, 0, 255, cv2.NORM_MINMAX, dtypecv2.CV_8U) # 应用伪彩色jet 最常用inferno 对高温更敏感 colored cv2.applyColorMap(temp_normalized, cv2.COLORMAP_JET) # 绘制 ROI 矩形与温度文本 cv2.rectangle(colored, (roi_x, roi_y), (roi_xroi_w, roi_yroi_h), (0,255,0), 2) cv2.putText(colored, f{roi_mean:.1f}°C, (roi_x10, roi_y30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,255), 2) # 显示需 X11 转发或本地运行 cv2.imshow(FLIR Radiometric Stream, colored) if cv2.waitKey(1) 0xFF ord(q): break # 退出循环 cv2.destroyAllWindows()性能优化要点cv2.normalize()比temp_img.astype(np.uint8)更快因后者会截断溢出值cv2.COLORMAP_INFERNO在 50–500°C 区间比 JET 更线性减少视觉误判若需保存视频用cv2.VideoWriter写入MJPG编码避免XVID在 Linux 下的色彩空间问题。6.2 多相机同步采集使用 FLIR 的硬件触发Trigger In与时间戳对齐# 启用外部触发模式需相机支持如 A655sc 的 GPIO Trigger In flir_lib.FLIR_SetTriggerMode.argtypes [ctypes.POINTER(FLIR_StreamHandle), ctypes.c_int] flir_lib.FLIR_SetTriggerMode(ctypes.byref(stream), 1) # 1 hardware trigger # 获取帧时间戳纳秒级用于多相机时间对齐 flir_lib.FLIR_GetFrameTimestamp.argtypes [ ctypes.POINTER(FLIR_StreamHandle), ctypes.POINTER(ctypes.c_uint64) ] flir_lib.FLIR_GetFrameTimestamp.restype ctypes.c_int timestamp_ns ctypes.c_uint64(0) flir_lib.FLIR_GetFrameTimestamp(ctypes.byref(stream), ctypes.byref(timestamp_ns)) print(fFrame timestamp: {timestamp_ns.value} ns since epoch)同步实践将两台 FLIR 相机的Trigger In引脚接同一脉冲信号如 PLC 输出确保曝光时刻一致用timestamp_ns作为时间轴基准与高速相机、激光测振仪等设备的时间戳做线性拟合校准注意FLIR_GetFrameTimestamp()返回的是主机接收时间非曝光开始时间高精度场景需用FLIR_GetExposureStartTime()SDK 4.0。6.3 自动 NUC 触发与状态监控避免长时间运行导致图像漂移# 查询当前 NUC 状态 flir_lib.FLIR_GetNUCStatus.argtypes [ ctypes.POINTER(FLIR_StreamHandle), ctypes.POINTER(ctypes.c_int) ] nuc_status ctypes.c_int(0) flir_lib.FLIR_GetNUCStatus(ctypes.byref(stream), ctypes.byref(nuc_status)) status_map {0:completed, 1:in_progress, 2:failed, -1:not_supported} print(fNUC status: {status_map.get(nuc_status.value, unknown)}) # 手动触发 NUC仅限支持型号如 A655sc flir_lib.FLIR_TriggerNUC.argtypes [ctypes.POINTER(FLIR_StreamHandle)] flir_lib.FLIR_TriggerNUC(ctypes.byref(stream)) # 异步执行需轮询 GetNUCStatus提示工业场景建议每 30 分钟自动触发一次 NUC尤其当环境温度变化 5°C 时Boson 相机支持FLIR_SetNUCInterval(1800)设置自动 NUC 间隔秒无需手动调用。本文还有配套的精品资源点击获取
返回列表