由于官方的sdk里的sample并没有详细写结合opencv开发的一些内容,在此做下入门记录,目标是在clion环境下,实时输出视频小窗。
另外,在使用过程中,发现GxRegisterCaptureCallback
(注册回调函数)和GxUnregisterCaptureCallback
(注销回调函数)不能用,不知道为什么,待解决,在之后的代码中我是用GxGetImage
来写的
官方的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.h
和GxIAPI.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);
}
}
#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
就可以跑了