前提:红外摄像厂商的SDK中只提供了在windows下的红外温度获取及解析的代码实例并且需要依托VS进行部署,那么如何在linux下实现这个功能?
准备资料:供应商的SDK、linux环境等
本次是以c++代码进行二次开发为例,具体代码如下:
1. 红外温度数据获取代码:
#include <iostream>
#include <cstring>
#include "HCNetSDK.h"
FILE* fTemData = NULL;
void CALLBACK g_RealDataCallBack_V30(LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, void* dwUser)
{
// 将获取到的全屏测温温度矩阵数据保存到本地文件,后面需要根据数据结构自行解析
switch (dwDataType)
{
case NET_DVR_SYSHEAD:
break;
case NET_DVR_STREAMDATA:
if (fTemData != NULL)
{
fwrite(pBuffer, dwBufSize, 1, fTemData);
}
break;
default:
break;
}
}
int main() {
//---------------------------------------
// 初始化
NET_DVR_Init();
//---------------------------------------
// 注册设备
LONG lUserID;
// 登录参数,包括设备地址、登录用户、密码等
NET_DVR_USER_LOGIN_INFO struLoginInfo = { 0 };
struLoginInfo.bUseAsynLogin = 0; // 同步登录方式
strcpy(struLoginInfo.sDeviceAddress, "xxx"); // 设备IP地址
struLoginInfo.wPort =xxx ; // 设备服务端口
strcpy(struLoginInfo.sUserName, "admin"); // 设备登录用户名
strcpy(struLoginInfo.sPassword, "xxx"// 设备登录密码
// 设备信息,输出参数
NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = { 0 };
lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);
if (lUserID < 0)
{
printf("Login failed, error code: %d\n", NET_DVR_GetLastError());
NET_DVR_Cleanup();
return 1;
}
//---------------------------------------
// 全屏测温参数配置
// 输入参数
NET_DVR_XML_CONFIG_INPUT struInput = { 0 };
struInput.dwSize = sizeof(struInput);
// 输出参数
NET_DVR_XML_CONFIG_OUTPUT struOutputParam = { 0 };
struOutputParam.dwSize = sizeof(struOutputParam);
char szUrl[512]; // URL
char szOutput[8 * 1024] = { 0 }; // 输出结果
char szStatusBuf[1024] = { 0 }; // 状态信息
char pBuf[2 * 1024] = { 0 };
// 获取参数
memset(szUrl, 0, sizeof(szUrl));
sprintf(szUrl, "%s", "GET /ISAPI/Thermal/channels/2/streamParam\r\n");
// 不同功能对应不同URL
struInput.lpRequestUrl = szUrl;
struInput.dwRequestUrlLen = strlen(szUrl);
// 获取时输入为空
struInput.lpInBuffer = NULL;
struInput.dwInBufferSize = 0;
// 分配输出内存
memset(szOutput, 0, sizeof(szOutput));
struOutputParam.lpOutBuffer = szOutput;
struOutputParam.dwOutBufferSize = sizeof(szOutput);
// 输出状态
memset(szStatusBuf, 0, sizeof(szStatusBuf));
struOutputParam.lpStatusBuffer = szStatusBuf;
struOutputParam.dwStatusSize = sizeof(szStatusBuf);
if (!NET_DVR_STDXMLConfig(lUserID, &struInput, &struOutputParam))
{
printf("NET_DVR_STDXMLConfig failed, error code: %d\n", NET_DVR_GetLastError());
}
else
{
printf("NET_DVR_STDXMLConfig successfully!\n");
printf("%s\n", szOutput);
}
// 设置参数
memset(szUrl, 0, sizeof(szUrl));
sprintf(szUrl, "%s", "PUT /ISAPI/Thermal/channels/2/streamParam\r\n");
// 输入JSON数据
memset(pBuf, 0, sizeof(pBuf));
strcpy(pBuf, "<ThermalStreamParam xmlns=\"http://www.isapi.org/ver20/XMLSchema\" version=\"2.0\">"
"<videoCodingType>pixel-to-pixel_thermometry_data</videoCodingType>"
"</ThermalStreamParam>");
// 输入参数
struInput.lpInBuffer = pBuf;
struInput.dwInBufferSize = sizeof(pBuf);
// 输出结果
memset(szOutput, 0, sizeof(szOutput));
struOutputParam.lpOutBuffer = szOutput;
struOutputParam.dwOutBufferSize = sizeof(szOutput);
// 输出状态
memset(szStatusBuf, 0, sizeof(szStatusBuf));
struOutputParam.lpStatusBuffer = szStatusBuf;
struOutputParam.dwStatusSize = sizeof(szStatusBuf);
if (!NET_DVR_STDXMLConfig(lUserID, &struInput, &struOutputParam))
{
printf("NET_DVR_STDXMLConfig failed, error code: %d\n", NET_DVR_GetLastError());
}
else
{
printf("NET_DVR_STDXMLConfig successfully!\n");
printf("lpOutBuffer: %s\n", szOutput);
printf("lpStatusBuffer: %s\n", szStatusBuf);
}
// 创建保存数据的文件
if (fTemData == NULL)
{
fTemData = fopen("RealTimeThermometry.data", "wb");
}
//---------------------------------------
// 启动预览并设置回调数据流
LONG lRealPlayHandle;
NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
struPlayInfo.hPlayWnd = (HWND)NULL; // 需要SDK解码时句柄设为有效值,仅取流不解码时可设为空
struPlayInfo.lChannel = 2; // 预览通道号
struPlayInfo.dwStreamType = 0; // 0-主码流,1-子码流,2-码流3,3-码流4,以此类推
struPlayInfo.dwLinkMode = 0; // 0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
struPlayInfo.byVideoCodingType = 1; // 码流数据编解码类型:0- 通用编码数据,1- 热成像数据
struPlayInfo.bBlocked = 1; // 0- 非阻塞取流,1- 阻塞取流
lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &struPlayInfo, g_RealDataCallBack_V30, NULL);
if (lRealPlayHandle < 0)
{
printf("NET_DVR_RealPlay_V40 error, %d\n", NET_DVR_GetLastError());
NET_DVR_Logout(lUserID);
NET_DVR_Cleanup();
return 1;
}
// 输入 q 退出程序,否则一直运行
char c = 0;
while ('q' != c)
{
printf("input 'q' to quit\n");
printf("input: ");
scanf("%c", &c);
}
//---------------------------------------
// 关闭预览
NET_DVR_StopRealPlay(lRealPlayHandle);
// 关闭文件,释放资源
if (fTemData != NULL)
{
fclose(fTemData);
fTemData = NULL;
}
// 注销用户
NET_DVR_Logout(lUserID);
NET_DVR_Cleanup();
return 0;
}
2. 温度数据获取后,解析代码如下:
#include <stdio.h>
#include <iostream>
#include <cstring>
#include <stdint.h>
#include "HCNetSDK.h"
using namespace std;
/*-----------------------------------------------------------*/
//全屏数据头部数据结构体定义
typedef struct
{
short year; /*APP->DSP 年*/
short month; /*APP->DSP 月*/
short dayOfWeek; /*APP->DSP 0:星期日-6:星期六*/
short day; /*APP->DSP 日*/
short hour; /*APP->DSP 小时*/
short minute; /*APP->DSP 分钟*/
short second; /*APP->DSP 秒*/
short milliSecond; /*APP->DSP 毫秒*/
}DATE_TIME;
typedef struct
{
UINT u32TmDataMode; /* 0为4字节,1为2字节 */
UINT u32TmScale; /* 测温缩放比例 */
UINT u32TmOffset; /* 测温偏移量,当前固定为0 */
UINT byIsFreezedata; /*是否是冻结数据,1:冻结,0:非冻结*/
}STREAM_FS_SUPPLE_INFO_TEMP;
typedef struct _STREAM_RT_DATA_INFO_S_
{
UINT u32RTDataType; // 1-14bit裸数据; 2-全屏测温结果数据; 3-YUV数据
UINT u32FrmNum;
UINT u32StdStamp; //DSP相对时间戳
DATE_TIME stTime; //绝对时间戳
UINT u32Width;
UINT u32Height;
UINT u32Len;
UINT u32Fps;
UINT u32Chan;
}STREAM_RT_DATA_INFO_S;
typedef struct _STREAM_FARME_INFO_TEMP_S_
{
UINT u32MagicNo; //0x70827773 "FRMI"的ascll码
UINT u32HeaderSize; //结构体长度
UINT u32StreamType; //数据类型: h264/h265, JPEG, Audio, MetaData, RTData: 参见 STREAM_TYPE_E
UINT u32StreamLen; //数据长度
STREAM_RT_DATA_INFO_S stRTDataInfo;
STREAM_FS_SUPPLE_INFO_TEMP stFsSuppleInfo;
UINT res[12];
UINT u32CrcVal; //结构体校验码 对结构体前面数据进行校验
}STREAM_FARME_INFO_TEMP;
/*-----------------------------------------------------------*/
/*在一段数据中找到结构体头的魔术字,并返回结构体头所在的位置*/
int FindMagicNo(char* buf, int buflen)
{
char magicNo[] = { (char)0x73, (char)0x77, (char)0x82, (char)0x70 }; //magicNo
char magicNoLen = sizeof(magicNo);
int dataIndex = 0;
int magicNoIndex = 0;
if (NULL == buf)
{
printf("FindMagicNo input buf is NULL\n");
return -1;
}
while (dataIndex <= buflen)
{
if (*(buf + dataIndex) == *(magicNo + magicNoIndex))
{
magicNoIndex++;
if (magicNoIndex == magicNoLen)
{
return (dataIndex - magicNoIndex + 1);
}
}
else
{
magicNoIndex = 0;
}
dataIndex++;
}
return -1;
}
/*-----------------------------------------------------------*/
/*从一个文件中找到第一个结构体的头,返回文件指针*/
int FindFirstHeadByFile(FILE* fp)
{
char* headBuf = NULL;
int headBufLen = 0;
int dataIndex = 0;
int fileIndex = 0;
headBufLen = 2 * sizeof(STREAM_FARME_INFO_TEMP);
headBuf = (char*)malloc(headBufLen);
memset(headBuf, 0, headBufLen);
//找到第一个结构体的头的位置
do
{
if (1 != fread(headBuf, headBufLen, 1, fp))
{
if (0 != feof(fp))
{
printf("GetRealtimeP2PDataByFile file has not a head!\n");
free(headBuf);
headBuf = NULL;
return -1;
}
printf("GetRealtimeP2PDataByFile read headBuf wrong\n");
free(headBuf);
headBuf = NULL;
return -1;
}
//查找魔术字
dataIndex = FindMagicNo(headBuf, headBufLen);
//没找到魔术字,向后退一个结构体大小
if (0 > dataIndex)
{
int iHeadSize = sizeof(STREAM_FARME_INFO_TEMP);
fseek(fp, -1 * iHeadSize, SEEK_CUR);
fileIndex++;
}
} while (0 > dataIndex);
free(headBuf);
headBuf = NULL;
return dataIndex + fileIndex * sizeof(STREAM_FARME_INFO_TEMP);
}
/*-----------------------------------------------------------*/
/*获取一帧数据*/
int GetRealtimeDataByBuf(char* inputBuf, int inputBufLen, char* outputBuf, int outputBufLen, STREAM_FARME_INFO_TEMP* pFarmeInfoTemp)
{
int dataIndex = 0;
if (NULL == inputBuf || NULL == outputBuf)
{
printf("input or output buf is NULL\n");
return -1;
}
dataIndex = FindMagicNo(inputBuf, inputBufLen);
if (0 <= dataIndex)
{
memcpy(pFarmeInfoTemp, inputBuf + dataIndex, sizeof(STREAM_FARME_INFO_TEMP));
//如果传入的数据不足一帧数据,则报错
if (inputBufLen < (dataIndex + sizeof(STREAM_FARME_INFO_TEMP) + pFarmeInfoTemp->u32StreamLen))
{
printf("inputBuf is not one frame data:%d(%d:%ld:%d)/%ld\n",
inputBufLen, dataIndex, sizeof(STREAM_FARME_INFO_TEMP), pFarmeInfoTemp->u32StreamLen,
(dataIndex + sizeof(STREAM_FARME_INFO_TEMP) + pFarmeInfoTemp->u32StreamLen));
return -1;
}
//如果传出数据缓存区的接收的长度小于数据长度,则报错
if (outputBufLen < pFarmeInfoTemp->u32StreamLen)
{
printf("outputBufLen is too short:%d/%d!\n", outputBufLen, pFarmeInfoTemp->u32StreamLen);
return -1;
}
//拷贝数据
memcpy(outputBuf, inputBuf + dataIndex + sizeof(STREAM_FARME_INFO_TEMP), pFarmeInfoTemp->u32StreamLen);
return pFarmeInfoTemp->u32StreamLen;
}
//如果返回值小于0则返回找MagicNo的错误码
return dataIndex;
}
/*-----------------------------------------------------------*/
/*全屏测温数据解析*/
int AnalysisRealtimeP2PData(char* inputBuf, int inputBufLen, STREAM_FARME_INFO_TEMP* pFarmeInfoTemp, char* saveFilePath)
{
int width = 0;
int height = 0;
int dataIndex = 4;
float fTemp = 0;
float fMaxTemp = -1000;
float fMinTemp = 1000;
FILE* filefp = NULL;
char temp[10] = { 0 };
if (NULL == inputBuf || NULL == pFarmeInfoTemp)
{
printf("AnalysisRealtimeP2PData input param is NULL");
return -1;
}
//判断传入数据长度时候正确(备注:由于获取到的数据前4个字节不是全屏测温数据,故减去)
if (inputBufLen - 4 != (pFarmeInfoTemp->stRTDataInfo.u32Width * pFarmeInfoTemp->stRTDataInfo.u32Height * pFarmeInfoTemp->stFsSuppleInfo.u32TmDataMode))
{
printf("AnalysisRealtimeP2PData input data wrong:%d/%d\n", inputBufLen,
(pFarmeInfoTemp->stRTDataInfo.u32Width * pFarmeInfoTemp->stRTDataInfo.u32Height * pFarmeInfoTemp->stFsSuppleInfo.u32TmDataMode));
return -1;
}
filefp = fopen(saveFilePath, "wb");
if (NULL == filefp)
{
printf("open file failed!\n");
return -1;
}
//解析数据,每个像素点进行温度值转换,2字节通过公式转换,4字节直接强转为float类型
for (height = 0; height < pFarmeInfoTemp->stRTDataInfo.u32Height; height++)
{
for (width = 0; width < pFarmeInfoTemp->stRTDataInfo.u32Width; width++)
{
memset(temp, 0, sizeof(temp));
if (2 == pFarmeInfoTemp->stFsSuppleInfo.u32TmDataMode)
{
fTemp = (*((unsigned short*)(inputBuf + dataIndex))) / (float)pFarmeInfoTemp->stFsSuppleInfo.u32TmScale + pFarmeInfoTemp->stFsSuppleInfo.u32TmOffset - 273.15;
dataIndex += 2;
}
else
{
fTemp = *((float*)(inputBuf + dataIndex));
dataIndex += 4;
}
fMaxTemp = (fMaxTemp > fTemp) ? fMaxTemp : fTemp;
fMinTemp = (fMinTemp > fTemp) ? fTemp : fMinTemp;
sprintf(temp, "%.2f,", fTemp);
if (1 != fwrite(temp, sizeof(temp), 1, filefp))
{
printf("write fTemp failed!\n");
fclose(filefp);
filefp = NULL;
return -1;
}
}
if (1 != fwrite("\n", 1, 1, filefp))
{
printf("write failed!\n");
fclose(filefp);
filefp = NULL;
return -1;
}
}
fclose(filefp);
filefp = NULL;
return 0;
}
int main() {
int Index = 0;
char* inputBuf = NULL;
int inputBufLen = 0;
char* outputBuf = NULL;
int outputBufLen = 0;
char fileName[512] = { 0 };
int count = 1;
STREAM_FARME_INFO_TEMP stFarmeInfoTemp = { 0 };
//打开本地保存的文件
FILE* datafilefp = fopen("RealTimeThermometry.data", "rb+");
if (NULL == datafilefp)
{
printf("Failed to open the file\n");
return -1;
}
Index = FindFirstHeadByFile(datafilefp);
if (0 > Index)
{
printf("GetRealtimeP2PDataByFile not find first head!\n");
return -1;
}
//文件指针移到第一个头的位置,并获取第一个头的信息
fseek(datafilefp, Index, SEEK_SET);
if (1 != fread(&stFarmeInfoTemp, sizeof(stFarmeInfoTemp), 1, datafilefp))
{
printf("read first head wrong\n");
return -1;
}
int iHeadSize = sizeof(STREAM_FARME_INFO_TEMP);
fseek(datafilefp, -1 * iHeadSize, SEEK_CUR);
inputBufLen = sizeof(STREAM_FARME_INFO_TEMP) + stFarmeInfoTemp.u32StreamLen;
inputBuf = (char*)malloc(inputBufLen);
outputBufLen = stFarmeInfoTemp.u32StreamLen;
outputBuf = (char*)malloc(outputBufLen);
while (1)
{
memset(inputBuf, 0, inputBufLen);
memset(outputBuf, 0, outputBufLen);
memset(&stFarmeInfoTemp, 0, sizeof(stFarmeInfoTemp));
if (1 != fread(inputBuf, inputBufLen, 1, datafilefp))
{
if (0 != feof(datafilefp))
{
break;
}
printf("read data wrong\n");
free(inputBuf);
inputBuf = NULL;
free(outputBuf);
outputBuf = NULL;
return -1;
}
if (0 > GetRealtimeDataByBuf(inputBuf, inputBufLen, outputBuf, outputBufLen, &stFarmeInfoTemp))
{
printf("get Data error!\n");
free(inputBuf);
inputBuf = NULL;
free(outputBuf);
outputBuf = NULL;
return -1;
}
//保存分析结果的文件路径
memset(fileName, 0, sizeof(fileName));
sprintf(fileName, "%d_%04d%02d%02d%02d%02d%02d_%04d.txt", stFarmeInfoTemp.stRTDataInfo.u32StdStamp,
stFarmeInfoTemp.stRTDataInfo.stTime.year, stFarmeInfoTemp.stRTDataInfo.stTime.month,
stFarmeInfoTemp.stRTDataInfo.stTime.day, stFarmeInfoTemp.stRTDataInfo.stTime.hour,
stFarmeInfoTemp.stRTDataInfo.stTime.minute, stFarmeInfoTemp.stRTDataInfo.stTime.second,
count++);
if (0 == stFarmeInfoTemp.stFsSuppleInfo.u32TmDataMode)
{
stFarmeInfoTemp.stFsSuppleInfo.u32TmDataMode = 4;
}
AnalysisRealtimeP2PData(outputBuf, outputBufLen, &stFarmeInfoTemp, fileName);
}
free(inputBuf);
inputBuf = NULL;
free(outputBuf);
outputBuf = NULL;
if (datafilefp != NULL)
{
fclose(datafilefp);
datafilefp = NULL;
}
printf("Analyze Successfully!\n");
return 0;
}
在linux下实现这个功能的步骤如下:
1. Linux获取及解析温度的源码已经修改完成,只需要把上述代码和供应商的sdk提供的so库拷贝到linux下即可,如下:
2. 直接执行命令make即可生成可执行程序(gettempdata是获取红外温度,analtempdata是把获取的红外温度数据解析成txt形式),具体如下:
3. 执行./ gettempdata 来获取红外温度的数据,此程序如果不键入“q”,会一直获取红外温度的数据,获取的红外温度数据报存在RealTimeThermometry.data中,具体如下:
4. 然后把获取的RealTimeThermometry.data放置在解析温度的代码文件夹下,执行./ analtempdata 即可生成解析出来的txt形式的温度数据,如下:
txt中温度数据如下:
至此,完成红外温度的获取及解析过程,下一步就可以根据获取的温度数据做自己的需求了(备注这个是根据厂商提供的SDK基础上开发的);
注意:如有侵权请联系我,我会及时删除掉;