怎样把mlx90640的输出显示为彩色的热成像图

0、热成像图


如你所见,下图就是一张人手的热成像图,可以看作是显示手部温度分布的图像。


怎样把mlx90640的输出显示为彩色的热成像图_第1张图片


1、伪彩色图和RGB图

在文章如何使用STM32F10x驱动MLX90640模块(移植官方驱动)中,咱们的讨论了怎么用Melexis的官方驱动读取Mlx90640的数据,但是咋个显示呢?

实际上,这是个灰度图转伪彩色图的问题。不清楚伪彩色是啥的,可以参考伪彩色_百科

我们从Mlx90640读出数据以后用官方驱动中的API函数MLX90640_CalculateTo()解算得到一个温度矩阵,mlx9064032×24的热成像芯片,所以温度矩阵也就是32×24的,如下图所示(单位为℃)。


怎样把mlx90640的输出显示为彩色的热成像图_第2张图片
这是个一维的矩阵,而我们熟悉的RGB图像呢,需要R(红)、G(绿)、B(蓝)三个分量的数据,也就是说,RGB图像可以看做一个有三个维度的结构,如下图所示:
怎样把mlx90640的输出显示为彩色的热成像图_第3张图片
灰度图转化的RGB彩图,就是伪彩图。


这样,任务就转化成两个步骤

  • 把温度矩阵转化为一维的灰度矩阵;
  • 把灰度矩阵转化为三维的RGB三个分量组成的结构。

2、灰度值转RGB

明明是一个值,怎么转成三个值呢?映射。
这个映射关系不太好找吧,咱查查论文呗,已经有人说的很清楚啦。

具体可以参考这篇文章:A perceptive uniform pseudo-color coding method of SAR images
主要就看下面这张图:
怎样把mlx90640的输出显示为彩色的热成像图_第4张图片
也就是三个步骤:

  • 灰度矩阵中的最大值

  • 计算HSI

/* 计算HSI */
float I = grey,H = (2*PI*grey)/L;
float S;
/* grey < L/2 */
if(grey<L/2){
	S = 1.5 * grey;
}else{
	S = 1.5 * (L-grey);
}
  • 计算RGB
/* 计算RGB */
float V1 = S* cos(H);
float V2 = S* sin(H);
float R = I - 0.204*V1 + 0.612*V2;
float G = I - 0.204*V1 - 0.612*V2;
float B = I + 0.408*V1; 

3、温度转灰度

知道灰度和RGB之间怎么转化了,那怎么把温度转成灰度呢?放缩。
Mlx90640采集的最高温度为300度,把(0,300)放缩到(0,200),用下面这段代码就行啦

for(int i=0;i<size;i++){
	float grey = (temp[i]*255)/300;
}


4、Code

所谓Talk is free,咱闲话少说,直接上代码哈


  • 一个函数Temp2RGB(float* temp,int size,float maxTemp,uint16_t* rgb)
void Temp2RGB(float* temp,int size,float maxTemp,uint16_t* rgb)
{
	double miniNum = 0.0002;
	float L = maxTemp;
	float PI = 3.14;
	for(int i=0;i<size;i++){
		/* 转温度为灰度 */
		float grey = (temp[i]*255)/300;
		/* 计算HSI */
		float I = grey,H = (2*PI*grey)/L;
		float S;
		/* grey < L/2 */
		if((grey-L/2) < miniNum){
		//if(grey
			S = 1.5 * grey;
		}else{
			S = 1.5 * (L-grey);
		}
		/* 计算RGB */
		float V1 = S* cos(H);
		float V2 = S* sin(H);
		float R = I - 0.204*V1 + 0.612*V2;
		float G = I - 0.204*V1 - 0.612*V2;
		float B = I + 0.408*V1; 
		/* 转为16bits RGB[5-6-5]色彩 */
		/* 
			(2^5-1)/(2^8-1) = 0.12 
			(2^6-1)/(2^8-1) = 0.24
		*/		
		uint16_t rbits = (R*0.125);
		uint16_t gbits = (G*0.250);
		uint16_t bbits = (B*0.125);
		rgb[i] = (rbits<<11)|(gbits<<5)|bbits;
	}
	
}

函数有四个参数:
temp:温度矩阵转成的一维向量,就是从API函数MLX90640_CalculateTo()得到的数组
sizetemp的长度
maxTemptemp中的最大值
rgb:保存rgb值的数组


5、疑问

转换函数Temp2RGB(float* temp,int size,float maxTemp,uint16_t* rgb)的定义中有一段


uint16_t rbits = (R*0.125);
uint16_t gbits = (G*0.250);
uint16_t bbits = (B*0.125);
rgb[i] = (rbits<<11)|(gbits<<5)|bbits;

这是啥呢?我们得到RGB值之后为啥要把三个数转成一个uint16_t类型的数呢?
这是为了显示到彩色OLED屏幕上。


怎样把mlx90640的输出显示为彩色的热成像图_第5张图片


屏幕显示彩色当然有编码方式呀,就是用三个颜色的值编成一个数据,那编码方式是咋样的呢?看一下屏幕模块里芯片的数据手册哈。


怎样把mlx90640的输出显示为彩色的热成像图_第6张图片


可以看到色彩数据编码的长度最长为18位,不够三个完整的八位的数据拼接。我们选16位的编码方式,也就是五位用于R,中间六位用于G,右侧五位用于B,如下图所示

怎样把mlx90640的输出显示为彩色的热成像图_第7张图片


五位最多表示32个不同的数,六位最多表示64个不同的数,除以八位最大表示数255分别约等于0.1250.250,可以乘这两个因子放缩

uint16_t rbits = (R*0.125);
uint16_t gbits = (G*0.250);
uint16_t bbits = (B*0.125);

然后移位,最后再通过或运算拼接起来,得到色彩编码。

rgb[i] = (rbits<<11)|(gbits<<5)|bbits;

好啦,完毕。

你可能感兴趣的:(从物理定律到编程语言,arm,热成像,伪色彩,编码,rgb)