linux获取红外温度数据及解析的源码二次开发

前提:红外摄像厂商的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基础上开发的);

注意:如有侵权请联系我,我会及时删除掉;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值