大恒MER-030工业相机+linux+opencv+clion-简易开发:让视频小窗跑出来

大恒工业相机usb2.0简易开发(一)

  • 前言
    • clion配置
    • 主函数代码
    • CTimeCounter.h

前言

由于官方的sdk里的sample并没有详细写结合opencv开发的一些内容,在此做下入门记录,目标是在clion环境下,实时输出视频小窗。

另外,在使用过程中,发现GxRegisterCaptureCallback(注册回调函数)和GxUnregisterCaptureCallback(注销回调函数)不能用,不知道为什么,待解决,在之后的代码中我是用GxGetImage来写的

最终效果:
大恒MER-030工业相机+linux+opencv+clion-简易开发:让视频小窗跑出来_第1张图片

clion配置

官方的sdk中只有makefile的配置,可以根据此来改写成cmakelist配置,代码如下:

cmake_minimum_required(VERSION 3.5)

project(GX_myJob_usb2_0) 
set(CMAKE_CXX_STANDARD 14)

# 相机要加的编译部分
set(CMAKE_EXE_LINKER_FLAGS
        -I$(GENICAM_ROOT_V2_3)/library/CPP/include
        -I$(DAHENG_ROOT)/sdk/include
        )

# 寻找必要依赖库
find_package(OpenCV 4 REQUIRED)

# 设置头文件目录
include_directories(${OpenCV_INCLUDE_DIRS})

# 添加可执行文件以及链接
add_executable(GX_myJob_usb2_0 main.cpp)
target_link_libraries(GX_myJob_usb2_0 ${OpenCV_LIBS})
target_link_libraries(GX_myJob_usb2_0
        -ldhgentl -lgxiapi -ldximageproc -lpthread
        -L$(GENICAM_ROOT_V2_3)/bin/Linux64_x64
        -L$(GENICAM_ROOT_V2_3)/bin/Linux64_x64/GenApi/Generic)

其中-ldhgentl -lgxiapi -ldximageproc -lpthread是必须加的,其他我删掉也没啥问题,但以防万一全加上了。

主函数代码

在下面 ProcGetImage 函数中写图像处理的部分即可,有很多函数在这里没用到(比如保存为PPM文件等),重点在 ProcGetImage中。
如果要用cmake的话,得把sdk里的DxImageProc.hGxIAPI.h也给拉进项目里。

#include "GxIAPI.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "DxImageProc.h"
#include "CTimeCounter.h"
using namespace std;
using namespace cv;

GX_DEV_HANDLE g_hDevice = NULL;                     ///< 设备句柄
GX_FRAME_DATA g_frameData;                          ///< 采集图像参数
pthread_t g_hThreadAcq;                             ///< 采集线程
bool g_bGetImg = false;                             ///< 采集线程是否结束的标志:true 运行;false 退出
int32_t g_nImageSize = 0;                           ///< 图像大小
void *g_pRaw8Buffer = NULL;                         ///< 将非8位raw数据转换成8位数据的时候的中转缓冲buffer
void *g_pRGBframeData = NULL;                       ///< RAW数据转换成RGB数据后的存储空间,大小是相机输出数据大小的3倍
int64_t g_nPixelFormat = GX_PIXEL_FORMAT_BAYER_GR8; ///< 当前相机的pixelformat格式
int64_t g_nColorFilter = GX_COLOR_FILTER_NONE;      ///< bayer插值的参数
void *g_pframeInfoData = NULL;                      ///< 帧信息数据缓冲区
size_t g_nframeInfoDataSize = 0;                    ///< 帧信息数据长度
CTimeCounter g_objTimeCounter;                      ///< 计时器
Mat test;                                           ///< 显示的图像

//查询固件版本号
void PrintFirmwareVersion(GX_FEATURE_ID featureID);

//查询FPGA版本号
void PrintFPGAVersion(GX_FEATURE_ID featureID);

//获取图像大小并申请图像数据空间
void PreGetImage();

//释放资源
void UnPreGetImage();

//采集线程函数
void *ProcGetImage(void* pParam);

//保存数据到PPM文件
void SavePPMFile(void *pImgBuffer, int32_t nWidth, int32_t nHeight);

//保存raw数据文件
void SaveRawFile(void *pImgBuffer, int32_t nWidth, int32_t nHeight);

//将相机输出的原始数据转换为RGB数据
void ProcessData(void * pImageBuf, void * pImageRaw8Buf, void * pImageRGBBuf, int nImageWidth, int nImageHeight, int nPixelFormat, int nPixelColorFilter);

//获取当前帧号
int GetCurFrameIndex();

//打印一个Mat矩阵
void PrintMat(Mat A)
{
    for(int i=0;i<A.rows;i++)
    {
        for(int j=0;j<A.cols;j++)
            cout<<A.at<float>(i,j)<<' ';
        cout<<endl;
    }
    cout<<endl;
}

int main()
{
    namedWindow("测试", 0);

    GX_STATUS status = GX_STATUS_SUCCESS;

    //初始化库
    status = GXInitLib();

    //枚举设备个数
    uint32_t nDeviceNum = 0;
    status = GXUpdateDeviceList(&nDeviceNum, 1000);

    if(nDeviceNum <=0)
    {
        printf("当前无可用设备!\n");
        return 0;
    }
    else
    {
        //默认打开第1个设备
        status = GXOpenDeviceByIndex(1, &g_hDevice);
        if(status == GX_STATUS_SUCCESS)
        {
            printf("打开设备成功!\n");
        }
        else
        {
            printf("打开设备失败!\n");
            return 0;
        }
    }

    //查询固件版本号
    PrintFirmwareVersion(GX_STRING_DEVICE_FIRMWARE_VERSION);

    //查询FPGA版本号
    PrintFPGAVersion(GX_STRING_DEVICE_VERSION);

    //设置采集模式为连续采集
    GXSetEnum(g_hDevice, GX_ENUM_ACQUISITION_MODE, GX_ACQ_MODE_CONTINUOUS);

    //设置触发开关为OFF
    GXSetEnum(g_hDevice, GX_ENUM_TRIGGER_MODE, GX_TRIGGER_MODE_OFF);

    //为采集做准备
    PreGetImage();

    //启动接收线程
    int ThreadId;
    ThreadId = pthread_create(&g_hThreadAcq, 0, ProcGetImage, 0);

    bool bRun = true;
    while(bRun)
    {
        int c = getchar();
        switch(c)
        {
            case 'X'://退出程序
            case 'x':
                bRun = false;
                break;

            default:;
        }
    }

    //为停止采集做准备
    UnPreGetImage();

    //关闭设备
    status = GXCloseDevice(g_hDevice);

    //释放库
    status = GXCloseLib();
    return 0;
}

//-------------------------------------------------
/**
\brief 查询固件版本号
\param featureID 功能码
\return void
*/
//-------------------------------------------------
void PrintFirmwareVersion(GX_FEATURE_ID featureID)
{
    char  pszContent[128] = {'\0'};
    size_t unSize = 128;
    GXGetString(g_hDevice, featureID, pszContent, &unSize);
    printf("固件版本:%s\n",pszContent);
}

//-------------------------------------------------
/**
\brief 查询FPGA版本号
\param featureID 功能码
\return void
*/
//-------------------------------------------------
void PrintFPGAVersion(GX_FEATURE_ID featureID)
{
    char  pszContent[128] = {'\0'};
    size_t unSize = 128;
    GXGetString(g_hDevice, featureID, pszContent, &unSize);
    printf("FPGA版本:%s\n",pszContent);
}

//-------------------------------------------------
/**
\brief 获取图像大小并申请图像数据空间
\return void
*/
//-------------------------------------------------
//-------------------------------------------------
/**
\brief 获取当前帧号
\return 当前帧号
*/
//-------------------------------------------------
int GetCurFrameIndex()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    status = GXGetBuffer(g_hDevice, GX_BUFFER_FRAME_INFORMATION, (uint8_t*)g_pframeInfoData, &g_nframeInfoDataSize);
    if(status != GX_STATUS_SUCCESS)
    {
        return -1;
    }
    int nCurIndex = 0;
    memcpy(&nCurIndex, (uint8_t*)g_pframeInfoData+14, sizeof(int));
    return nCurIndex;
}

//-------------------------------------------------
/**
\brief 获取图像大小并申请图像数据空间
\return void
*/
//-------------------------------------------------
void PreGetImage()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    int64_t nPayLoadSize = 0;
    status = GXGetInt(g_hDevice, GX_INT_PAYLOAD_SIZE, &nPayLoadSize);
    g_frameData.pImgBuf = malloc(nPayLoadSize);
    g_nImageSize = nPayLoadSize;

    //将非8位raw数据转换成8位数据的时候的中转缓冲buffer
    g_pRaw8Buffer = malloc(nPayLoadSize);

    //RGB数据是RAW数据的3倍大小
    g_pRGBframeData = malloc(nPayLoadSize * 3);

    //获取相机输出数据的颜色格式
    status = GXGetEnum(g_hDevice, GX_ENUM_PIXEL_FORMAT, &g_nPixelFormat);

    status = GXGetEnum(g_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &g_nColorFilter);

    //接收线程启动标志
    g_bGetImg = true;

    //设置采集速度级别=======================================================================================
    GXSetInt(g_hDevice, GX_INT_ACQUISITION_SPEED_LEVEL, 3);
    //设置AOI
    int64_t nOffsetX = 0;
    int64_t nOffsetY = 0;
    int64_t nWidth   = 1292;
    int64_t nHeight  = 964;
    status = GXSetInt(g_hDevice, GX_INT_OFFSET_X, nOffsetX);
    status = GXSetInt(g_hDevice, GX_INT_OFFSET_Y, nOffsetY);
    status = GXSetInt(g_hDevice, GX_INT_WIDTH, nWidth);
    status = GXSetInt(g_hDevice, GX_INT_HEIGHT, nHeight);
    //设置曝光时间
    status = GXSetFloat(g_hDevice, GX_FLOAT_EXPOSURE_TIME, 60000);

    //发送开采命令
    status = GXSendCommand(g_hDevice, GX_COMMAND_ACQUISITION_START);

    //获取帧信息长度并申请帧信息数据空间
    GXGetBufferLength(g_hDevice, GX_BUFFER_FRAME_INFORMATION, &g_nframeInfoDataSize);
    g_pframeInfoData = malloc(g_nframeInfoDataSize);
}

//-------------------------------------------------
/**
\brief 释放资源
\return void
*/
//-------------------------------------------------
void UnPreGetImage()
{
    GX_STATUS status = GX_STATUS_SUCCESS;

    g_bGetImg = false;
    pthread_join(g_hThreadAcq,NULL);

    //发送停采命令
    status = GXSendCommand(g_hDevice, GX_COMMAND_ACQUISITION_STOP);

    //释放buffer
    free(g_frameData.pImgBuf);
    g_frameData.pImgBuf = NULL;
    free(g_pRaw8Buffer);
    g_pRaw8Buffer = NULL;
    free(g_pRGBframeData);
    g_pRGBframeData = NULL;
    free(g_pframeInfoData);
    g_pframeInfoData = NULL;
}

//-------------------------------------------------
/**
\brief 采集线程函数
\param pParam 线程传入参数
\return void*
*/
//-------------------------------------------------
void *ProcGetImage(void* pParam)
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    while(g_bGetImg)
    {
        if(g_frameData.pImgBuf == NULL)
        {
            continue;
        }

        status = GXGetImage(g_hDevice, &g_frameData, 100);
        if(status == 0)
        {
            if(g_frameData.nStatus == 0)
            {
                printf("采集成功: 宽:%d 高:%d\n", g_frameData.nWidth, g_frameData.nHeight);
                printf("帧号:%d\n", GetCurFrameIndex());
                
//                //保存Raw数据
//                SaveRawFile(g_frameData.pImgBuf, g_frameData.nWidth, g_frameData.nHeight);
//
                //将Raw数据处理成RGB数据
                ProcessData(g_frameData.pImgBuf,
                            g_pRaw8Buffer,
                            g_pRGBframeData,
                            g_frameData.nWidth,
                            g_frameData.nHeight,
                            g_nPixelFormat,
                            g_nColorFilter);
//
//                //保存RGB数据
//                SavePPMFile(g_pRGBframeData, g_frameData.nWidth, g_frameData.nHeight);

                test.create( g_frameData.nHeight, g_frameData.nWidth, CV_8UC3);
                memcpy(test.data, g_pRGBframeData, g_frameData.nHeight*g_frameData.nWidth*3);
//                printf("ready? go!\n");
                PrintMat(test);
//
                namedWindow("测试", 0);
                imshow("测试", test);
//
                waitKey(20);  // 等待任意按键按下
//
//
//                printf("TYPE IS %s\n",typeid(g_pRGBframeData).name());

            }
            else
            {
                printf("采集异常:异常码:%d\n", g_frameData.nStatus);
            }

        }
    }
}

//----------------------------------------------------------------------------------
/**
\brief  将相机输出的原始数据转换为RGB数据
\param  [in] pImageBuf  指向图像缓冲区的指针
\param  [in] pImageRaw8Buf  将非8位的Raw数据转换成8位的Raw数据的中转缓冲buffer
\param  [in,out] pImageRGBBuf  指向RGB数据缓冲区的指针
\param  [in] nImageWidth 图像宽
\param  [in] nImageHeight 图像高
\param  [in] nPixelFormat 图像的格式
\param  [in] nPixelColorFilter Raw数据的像素排列格式
\return 无返回值
*/
//----------------------------------------------------------------------------------
void ProcessData(void * pImageBuf, void * pImageRaw8Buf, void * pImageRGBBuf, int nImageWidth, int nImageHeight, int nPixelFormat, int nPixelColorFilter)
{
    switch(nPixelFormat)
    {
        //当数据格式为12位时,位数转换为4-11
        case GX_PIXEL_FORMAT_BAYER_GR12:
        case GX_PIXEL_FORMAT_BAYER_RG12:
        case GX_PIXEL_FORMAT_BAYER_GB12:
        case GX_PIXEL_FORMAT_BAYER_BG12:
            //将12位格式的图像转换为8位格式
            DxRaw16toRaw8(pImageBuf, pImageRaw8Buf, nImageWidth, nImageHeight, DX_BIT_4_11);
            //将Raw8图像转换为RGB图像以供显示
            DxRaw8toRGB24(pImageRaw8Buf, pImageRGBBuf, nImageWidth, nImageHeight,RAW2RGB_NEIGHBOUR,
                          DX_PIXEL_COLOR_FILTER(nPixelColorFilter),false);
            break;

            //当数据格式为10位时,位数转换为2-9
        case GX_PIXEL_FORMAT_BAYER_GR10:
        case GX_PIXEL_FORMAT_BAYER_RG10:
        case GX_PIXEL_FORMAT_BAYER_GB10:
        case GX_PIXEL_FORMAT_BAYER_BG10:
            将10位格式的图像转换为8位格式,有效位数2-9
            DxRaw16toRaw8(pImageBuf, pImageRaw8Buf, nImageWidth, nImageHeight, DX_BIT_2_9);
            //将Raw8图像转换为RGB图像以供显示
            DxRaw8toRGB24(pImageRaw8Buf, pImageRGBBuf, nImageWidth, nImageHeight,RAW2RGB_NEIGHBOUR,
                          DX_PIXEL_COLOR_FILTER(nPixelColorFilter),false);
            break;

        case GX_PIXEL_FORMAT_BAYER_GR8:
        case GX_PIXEL_FORMAT_BAYER_RG8:
        case GX_PIXEL_FORMAT_BAYER_GB8:
        case GX_PIXEL_FORMAT_BAYER_BG8:
            //将Raw8图像转换为RGB图像以供显示
            g_objTimeCounter.Begin();
            DxRaw8toRGB24(pImageBuf,pImageRGBBuf, nImageWidth, nImageHeight,RAW2RGB_NEIGHBOUR,
                          DX_PIXEL_COLOR_FILTER(nPixelColorFilter),false);//RAW2RGB_ADAPTIVE
            printf("DxRaw8toRGB24 耗时:%ld\n", g_objTimeCounter.End());
            break;

        case GX_PIXEL_FORMAT_MONO12:
            //将12位格式的图像转换为8位格式
            DxRaw16toRaw8(pImageBuf, pImageRaw8Buf, nImageWidth, nImageHeight, DX_BIT_4_11);
            //将Raw8图像转换为RGB图像以供显示
            DxRaw8toRGB24(pImageRaw8Buf, pImageRGBBuf, nImageWidth, nImageHeight, RAW2RGB_NEIGHBOUR,
                          DX_PIXEL_COLOR_FILTER(NONE),false);
            break;

        case GX_PIXEL_FORMAT_MONO10:
            //将10位格式的图像转换为8位格式
            DxRaw16toRaw8(pImageBuf, pImageRaw8Buf, nImageWidth, nImageHeight, DX_BIT_4_11);
            //将Raw8图像转换为RGB图像以供显示
            DxRaw8toRGB24(pImageRaw8Buf, pImageRGBBuf, nImageWidth, nImageHeight,RAW2RGB_NEIGHBOUR,
                          DX_PIXEL_COLOR_FILTER(NONE),false);
            break;

        case GX_PIXEL_FORMAT_MONO8:
            //将Raw8图像转换为RGB图像以供显示
            DxRaw8toRGB24(pImageBuf, pImageRGBBuf, nImageWidth, nImageHeight,RAW2RGB_NEIGHBOUR,
                          DX_PIXEL_COLOR_FILTER(NONE),false);
            break;

        default:
            break;
    }
}



//-------------------------------------------------
/**
\brief 保存内存数据到ppm格式文件中
\param pImgBuffer RAW数据经过插值换算出的RGB数据
\param nWidth 图像宽
\param nHeight 图像高
\return void
*/
//-------------------------------------------------
void SavePPMFile(void *pImgBuffer, int32_t nWidth, int32_t nHeight)
{
    char name[64] = {0};

    static int nRGBFileIndex = 1;
    FILE* ff = NULL;

    sprintf(name, "RGB%d.ppm", nRGBFileIndex++);
    ff=fopen(name,"wb");
    if(ff)
    {
        fprintf(ff, "P6\n" "%d %d\n255\n", nWidth, nHeight);
        fwrite(pImgBuffer, 1, nWidth * nHeight * 3, ff);
        fclose(ff);
        printf("保存%s成功\n", name);
    }
}

//-------------------------------------------------
/**
\brief 保存raw数据文件
\param pImgBuffer raw图像数据
\param nWidth 图像宽
\param nHeight 图像高
\return void
*/
//-------------------------------------------------
void SaveRawFile(void *pImgBuffer, int32_t nWidth, int32_t nHeight)
{
    char name[64] = {0};

    static int nRawFileIndex = 1;
    FILE* ff = NULL;

    sprintf(name, "RAW%d.ppm", nRawFileIndex++);
    ff=fopen(name,"wb");
    if(ff)
    {
        fprintf(ff, "P5\n" "%d %d 255\n", nWidth, nHeight);
        fwrite(pImgBuffer, 1, nWidth * nHeight, ff);
        fclose(ff);
        printf("保存%s成功\n", name);
    }
}

CTimeCounter.h

#include
#include

class CTimeCounter  
{
public: 
	CTimeCounter() 
	{ 
		Begin(); 
	} 
	void Begin() 
	{ 
		gettimeofday(&tvBegin, &tzBegin);
	} 
	long End() 
	{ 
		gettimeofday(&tvEnd, &tzEnd);
		return ((tvEnd.tv_sec * 1000000 + tvEnd.tv_usec) - (tvBegin.tv_sec * 1000000 + tvBegin.tv_usec)) / 1000; 
	} 
protected: 
	struct timeval  tvBegin;
	struct timezone tzBegin;
	struct timeval  tvEnd;	
	struct timezone tzEnd;	
};

在终端 cmake . 然后 make 就可以跑了

你可能感兴趣的:(工业相机开发,c++,linux,ubuntu)