大疆热红外图片温度读取

适用于 大疆禅思 Zenmuse H20 系列拍摄的红外图片,依赖于大疆发布的TSDK(DJI Thermal SDK),可用于视频的动态测温。

其实对TSDK并未深入研究,只是某一个小项目要使用TSDK处理H20拍摄的红外照片,利用TSDK中的sample又进行了一些处理,能够实现将对应像素、指定区域的温度进行读取。

大疆社区对这个TSDK的使用描述较少,为了任务只好硬着头皮上。

首先,对TSDK中的sample进行编译,这里有一点,默认编译脚本使用vs2015,但是我这里只安装了vs2017,只需要找到build.bat,将"Visual Studio 14 2015"改为"Visual Studio 15 2017"保存即可。

编译生成的程序中,我只用到了“dji_irp.exe”这一个,使用的参数为

"dji_irp.exe" -s "image/file/path" -a measure -o "path/of/raw/output"

其他命令和参数可以使用-help查看,上边省略的参数是INT32,即使用16位数据保存一个温度*10的整数,对得到的数值*0.1即为0.1精度的真实温度。

它可以将红外图片生成为16位raw文件,原来没有很明白,后来才渐渐搞懂,也就是说,每16位(2字节)对应原始图片的一个像素。起初对jpg格式的保存方式也不太了解,只好一点一点尝试。

好在方向没有搞错,大致保存方式为从左上角0,0开始,一行一行的存储,所以raw文件中也是如此保存的了。

这里也要说明一下,我确认没有搞错方向的依据是与大疆发布的大疆红外热分析工具的像素温度、最高最低温度点保持一致。

我定义了一个类RawParser来解析生成的raw文件数据,主要功能有:

1、获取指定点坐标的温度

2、获取指定区域的平均温度

3、获取指定区域最高、最低温度及其坐标

先上代码。

RawParser.h

#pragma once
#include <string>
#define IN
#define OUT

struct RectArea
{
	int x;
	int y;
	int w;
	int h;
};

struct PixPoint
{
	int x;
	int y;
};

struct PixTemperature
{
	PixPoint point;
	int temperature;//tenfold
};

class RawParser
{
public:

	RawParser(const char* filePath, int width, int height);
	~RawParser();

	//将raw文件内容加载到内存中再进行操作和计算
	bool preloadRaw();

	//将像素点的坐标转换为内存索引
	long pointToIndex(int x, int y);
	//通过像素的坐标xy获取对应的温度,temperature为10倍温度的整数(温度取1位有效位)
	bool getPointTemperature(int x IN, int y IN, int &temperature OUT);//tenfold
	//计算指定范围内像素点温度的平均值、最高值、最高值坐标、最低值、最低值坐标
	bool rangeCaculate(RectArea rect IN, float &temperature OUT, PixTemperature &maxPT OUT, PixTemperature &minPT OUT);
	//rect的宽高应大于0
	bool rangeCaculate(RectArea rect IN, float &temperature OUT);
	
private:
	FILE *m_imageFP;
	int m_imageW;
	int m_imageH;
	size_t m_fileSize;
	bool m_bHasPreload;
	std::string m_rawData;
};

RawParser.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "RawParser.h"

#define RAW_LENGTH 16

typedef unsigned char BYTE;

RawParser::RawParser(const char* filePath, int width, int height)
	:m_imageW(width)
	,m_imageH(height)
	,m_bHasPreload(false)
{
	m_imageFP = nullptr;

	if ((m_imageFP = fopen(filePath, "rb")) == nullptr)
	{
		m_fileSize = 0;
		printf("can not open the raw image");
		return;
	}
	else
	{
		//读取文件总大小
		fseek(m_imageFP, 0, SEEK_END);
		m_fileSize = ftell(m_imageFP);
		fseek(m_imageFP, 0, SEEK_SET);
	}
}


RawParser::~RawParser()
{
	if (m_imageFP)
	{
		fclose(m_imageFP);
	}
}

bool RawParser::preloadRaw()
{
	char* _content = (char*)malloc(m_fileSize);
	if (!_content)
	{
		return false;
	}

	size_t _cnt = fread(_content, 1, m_fileSize, m_imageFP);
	if (_cnt != m_fileSize)
	{
		free(_content);
		return false;
	}
	m_rawData.assign(_content, _cnt);
	m_bHasPreload = true;
	return true;
}

bool RawParser::getPointTemperature(int x, int y, int &temperature)
{
	if (m_bHasPreload)
	{
		if (m_rawData.empty())
		{
			return false;
		}
	}
	else
	{
		if (!m_imageFP)
		{
			return false;
		}
	}
	if (m_imageW <= x)
	{
		return false;
	}
	if (m_imageH <= y)
	{
		return false;
	}
	unsigned char tmpValue[2];
	long _offset = (m_imageW*y + x) * 2;//每个温度数据占用两个字节16位
	if (m_fileSize < _offset)
	{
		return false;
	}

	if(m_bHasPreload)
	{
		tmpValue[0] = m_rawData.at(_offset);
		tmpValue[1] = m_rawData.at(_offset + 1);
	}
	else
	{
		fseek(m_imageFP, _offset, 0);
		//从像素坐标对应的文件位置读出两个字节数据
		size_t _ret = fread(tmpValue, 1, 2, m_imageFP);
		if (_ret != 2)
		{
			return false;
		}
	}

	temperature = tmpValue[1];
	//字节序转换
	temperature = temperature << 8;
	temperature = temperature + (tmpValue[0]);
	return true;
}

bool RawParser::rangeCaculate(RectArea rect IN, float &temperature OUT, PixTemperature &maxPT OUT, PixTemperature &minPT OUT)
{
	if (m_bHasPreload)
	{
		if (m_rawData.empty())
		{
			return false;
		}
	}
	else
	{
		if (!m_imageFP)
		{
			return false;
		}
	}
	if (   rect.x < 0
		|| rect.y < 0
		|| rect.w < 0
		|| rect.h < 0
		|| rect.x > m_imageW
		|| rect.y > m_imageH
		|| (rect.x+rect.w) > m_imageW
		|| (rect.y+rect.h) > m_imageH)
	{
		return false;
	}
	PixPoint maxTPP, minTPP,bottomRight;
	int maxTemp, minTemp;
	bool pointsReady = false;//最高最低温度初始化标记
	//fseek(m_imageFP, 0, 0);
	long pointCount = 0;
	unsigned char _temp[2];
	//所有点温度之和。使用__int64保存结果足够保证计算时不会溢出,
	//温度保存16位,范围是±32767,_int64表示范围是±9,223,372,036,854,775,808
	//按照最大值计算,可保存281,474,976,710,656个数据之和
	//一张1080*1080的图片只有1,166,400个像素
	__int64 _temperature = 0;
	int pointTemp;//点温度
	bottomRight.x = rect.x + rect.w;
	bottomRight.y = rect.y + rect.h;
	for (int x = rect.x; x < bottomRight.x;++x)
	{
		for (int y = rect.y; y < bottomRight.y; ++y)
		{
			long _index = pointToIndex(x, y);
			if (m_fileSize < _index)
			{
				return false;
			}

			if (m_bHasPreload)
			{
				_temp[0] = m_rawData.at(_index);
				_temp[1] = m_rawData.at(_index + 1);
			}
			else
			{
				fseek(m_imageFP, _index, 0);
				fread(_temp, 1, 2, m_imageFP);
			}

			pointTemp = ((_temp[1] << 8) & 0xFF00);
			pointTemp += _temp[0];
			//判断最高、最低温度并记录坐标
			if (pointsReady)
			{
				if (pointTemp < minTemp)
				{
					minTemp = pointTemp;
					minTPP.x = x;
					minTPP.y = y;
				}
				else if (pointTemp > maxTemp)
				{
					maxTemp = pointTemp;
					maxTPP.x = x;
					maxTPP.y = y;
				}
			}
			else
			{
				//最高、最低温度初始化为第一个点的温度
				minTemp = pointTemp;
				minTPP.x = x;
				minTPP.y = y;
				maxTemp = pointTemp;
				maxTPP.x = x;
				maxTPP.y = y;
				pointsReady = true;
			}
			_temperature += pointTemp;
			++pointCount;
		}
	}
	if (pointCount == 0)
	{
		return false;
	}
	temperature = _temperature / pointCount * 0.1;
	maxPT.point = maxTPP;
	maxPT.temperature = maxTemp;
	minPT.point = minTPP;
	minPT.temperature = minTemp;
	return true;
}

bool RawParser::rangeCaculate(RectArea rect IN, float &temperature OUT)
{
	PixTemperature maxPT, minPT;
	return rangeCaculate(rect, temperature, maxPT, minPT);
}

long RawParser::pointToIndex(int x, int y)
{
	if (m_imageW <= x)
	{
		return false;
	}
	return ((m_imageW*y + x) * 2);//每个温度数据占用两个字节16位
}

起初摸索的时候采用了文件指针偏移的方法实时从文件读取,效率非常低,后来直接把文件全部读到内存中再计算,效率就提高很多,640*512大小的图片,一次全图计算大概只需要500ms。

其中关键的部分就是找对像素坐标对应的数据位置,其他就简单多了。

后边附上Qt写的调用Demo,感兴趣的可以参考一下。

===

补充一下,RawParser这个类中并未使用依赖平台的库或头文件,只是简单的数学和逻辑运算,基本上可以轻松移植。写这个东西本来就是研究使用方法,给其他同事参考的,他是用的java,没有遇到什么问题。

  • 9
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 27
    评论
红外成像温度提取是利用红外像仪采集到的像数据,通过一定的算法将其转化为对应的温度信息。在Python中,可以使用一些开源库和函数来实现红外成像温度提取。 首先,需要导入相应的库,例如OpenCV和Numpy: ``` import cv2 import numpy as np ``` 接下来,读取像数据,可以使用OpenCV库中的`cv2.imread()`函数: ``` image = cv2.imread("thermal_image.jpg") ``` 红外像数据通常是以灰度图像的形式存在的,可以将其转化为灰度图像: ``` gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) ``` 灰度图像中的亮度值与温度存在一定的关系,可以通过一些公式来将亮度值转化为对应的温度值。常见的转化公式包括线性公式和非线性公式,具体的选取取决于像仪的型号和厂商。以下是一个示例的线性转化公式: ``` temperature = gray_image * 0.1 ``` 上述公式中的0.1是一个常数,用于将亮度值映射到温度范围。 最后,可以将提取到的温度信息进行可视化展示,可以使用Matplotlib库中的函数来实现: ``` import matplotlib.pyplot as plt plt.imshow(temperature, cmap='hot') plt.colorbar() plt.show() ``` 上述代码将温度信息以彩色图像的形式展示出来,并添加一个温度色标。 综上所述,以上介绍了如何使用Python实现红外成像温度提取的基本步骤。除了上述方法外,还可以根据具体需求和数据格式,选择不同的库和函数来实现温度提取和可视化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值