如你所见,下图就是一张人手的热成像图,可以看作是显示手部温度分布的图像。
在文章如何使用STM32F10x驱动MLX90640模块(移植官方驱动)中,咱们的讨论了怎么用Melexis
的官方驱动读取Mlx90640的数据,但是咋个显示呢?
实际上,这是个灰度图转伪彩色图的问题。不清楚伪彩色是啥的,可以参考伪彩色_百科
我们从Mlx90640
读出数据以后用官方驱动中的API
函数MLX90640_CalculateTo()
解算得到一个温度矩阵,mlx90640
是32×24
的热成像芯片,所以温度矩阵也就是32×24
的,如下图所示(单位为℃)。
这是个一维的矩阵,而我们熟悉的RGB
图像呢,需要R
(红)、G
(绿)、B
(蓝)三个分量的数据,也就是说,RGB
图像可以看做一个有三个维度的结构,如下图所示:
灰度图转化的RGB
彩图,就是伪彩图。
这样,任务就转化成两个步骤:
明明是一个值,怎么转成三个值呢?映射。
这个映射关系不太好找吧,咱查查论文呗,已经有人说的很清楚啦。
具体可以参考这篇文章:A perceptive uniform pseudo-color coding method of SAR images
主要就看下面这张图:
也就是三个步骤:
找灰度矩阵中的最大值
计算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;
知道灰度和RGB
之间怎么转化了,那怎么把温度转成灰度呢?放缩。
Mlx90640采集的最高温度为300度,把(0,300)
放缩到(0,200)
,用下面这段代码就行啦
for(int i=0;i<size;i++){
float grey = (temp[i]*255)/300;
}
所谓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()
得到的数组
size
:temp
的长度
maxTemp
:temp
中的最大值
rgb
:保存rgb
值的数组
转换函数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屏幕上。
屏幕显示彩色当然有编码方式呀,就是用三个颜色的值编成一个数据,那编码方式是咋样的呢?看一下屏幕模块里芯片的数据手册哈。
可以看到色彩数据编码的长度最长为18
位,不够三个完整的八位的数据拼接。我们选16位的编码方式,也就是五位用于R
,中间六位用于G
,右侧五位用于B
,如下图所示
五位最多表示32
个不同的数,六位最多表示64
个不同的数,除以八位最大表示数255
分别约等于0.125
和0.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;
好啦,完毕。