ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

大恒MER-1070-10GC相机 LINUX环境 QT开发记录

2021-11-19 19:32:58  阅读:288  来源: 互联网

标签:status STATUS 10GC 1070 GX 相机 Camera cam MER


大恒工业相机MER-1070-10GC开发记录

目录

前言

实验室正好有一个大恒型号为MER-1070-10GC的面阵相机,于是便用该相机学习工业相机的开发,写下本篇博客作为开发记录,也当作学习之路的一次小小实践。
本次开发环境为Ubuntu18,开发软件为QT5,配合Opencv进行图像处理。主要完成相机的基本配置、初始化及打开,并且通过回调函数的方法获得图片,显示在UI界面。同时Opencv获取到图片,可以根据自己的要求进行图像处理。
在这里插入图片描述

相机参数介绍

详情见大恒相机官方文档 水星系列GIgE应用说明书

名称参数
型号MER-1070-10GC
分辨率3840 × \times × 2748
像素深度8bit、12bit
快门时间42 μ \mu μ ~ 1s
图像数据格式Bayer GR8 / Bayer GR12
输入输出接口1个光耦输入接口,一个光耦输出接口,2个双向GPIO
数据接口百兆以太网或千兆以太网

光谱相应图

在这里插入图片描述

前期准备

SDK下载

首先前往大恒相机官网下载相应软件开发SDK 大恒相机SDK下载地址。根据需求选择相应的选项,本次开发采用C/C++语言,因此下载第一个选项。
在这里插入图片描述下载完成后在Linux中解压,在opt目录下有Galaxy_Linux-x86_Gige-U3_32bits-64bits_1.2.1911.9122文件夹,运行.run文件进行安装。bin文件夹内有客户端程序GalaxyView和IP配置软件GxGigeIPConfig,还有动态链接库文件头文件在inc文件夹内,doc文件夹内有C软件开发说明书,里面有详细操作及编程介绍至此,SDK和客户端软件下载完成
在这里插入图片描述

客户端软件调试

相机上电,通过网线连接电脑,首先通过IP配置软件给相机配置IP地址,然后打开GalaxyView测试相机。在该软件中,可以调整相机的曝光、增益、白平衡、ROI区域、触发模式、采集模式等参数,最后可以导出配置文件,为后续开发提供便捷。
在这里插入图片描述

开发流程

C软件开发说明书已经上传到我的资源 大恒相机C软件开发说明书

1 QT配置文件

在QT正式开发前,需要将上述下载的API库加入.pro配置文件,否则是无法在程序中调用相机SDK的,同时将Opencv库加入配置文件。

# camera lib
LIBS += -L$$PWD/../../../../../usr/lib/libgxiapi.so -lgxiapi

INCLUDEPATH += $$PWD/../../../../../opt/Galaxy_Linux-x86_Gige-U3_32bits-64bits_1.2.1911.9122/Galaxy_camera/inc
DEPENDPATH += $$PWD/../../../../../opt/Galaxy_Linux-x86_Gige-U3_32bits-64bits_1.2.1911.9122/Galaxy_camera/inc

# opecncv
LIBS += -L/usr/lib/x86_64-linux-gnu/ -lopencv_core
LIBS += -L/usr/lib/x86_64-linux-gnu/ -lopencv_highgui
LIBS += -L/usr/lib/x86_64-linux-gnu/ -lopencv_imgcodecs
LIBS += -L/usr/lib/x86_64-linux-gnu/ -lopencv_video
LIBS += -L/usr/lib/x86_64-linux-gnu/ -lopencv_videoio
LIBS += -L/usr/lib/x86_64-linux-gnu/ -lopencv_shape
LIBS += -L/usr/lib/x86_64-linux-gnu/ -lopencv_imgproc

INCLUDEPATH += /usr/include/opencv2
DEPENDPATH += /usr/include/opencv2

2 相机初始化

2.1 创建相机类

为了方便相机操作,创建相机类。

class Camera
{
public:
    Camera();
    uint32_t Enum_Camera();  //枚举相机  
    bool Open_Camera();      //打开相机
    bool Close_Camera();     //关闭相机
    bool Init_Camera();      //初始化相机
    /*注册回调函数*/
    bool Register_Callback_Fun();
    /*注销回调函数*/
    bool Unregister_Callback_Fun();  
    GX_DEV_HANDLE cam_handle = nullptr;  //相机句柄
};

定义成员函数

#include "camera.h"
#include <QDebug>
#define DEVICE_INDEX_TO_OPEN "1"
extern void GX_STDC OnFrameCallbackFun(GX_FRAME_CALLBACK_PARAM* pFrame);

Camera::Camera()
{
}

uint32_t Camera::Enum_Camera()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    status = GXInitLib();   //初始化库
    uint32_t nDeviceNum = 0;
    status = GXUpdateDeviceList(&nDeviceNum, 1000);
    if(status == GX_STATUS_SUCCESS && nDeviceNum > 0)
    {
        GX_DEVICE_BASE_INFO *pBaseinfo = new GX_DEVICE_BASE_INFO[nDeviceNum];
        size_t nSize = nDeviceNum * sizeof(GX_DEVICE_BASE_INFO);
        status = GXGetAllDeviceBaseInfo(pBaseinfo, &nSize);
        if(status == GX_STATUS_SUCCESS)
        {
            for(uint32_t i=0; i<nDeviceNum; i++)
            {
                qDebug() << "No: " << i+1 << " camera enum successfully!";
                qDebug() << "szDeviceID: " << pBaseinfo[i].szDeviceID;
                qDebug() << "szDisplayName: " << pBaseinfo[i].szDisplayName;
            }
            delete []pBaseinfo;
            return nDeviceNum;
        }
    }
    else
        qDebug() << "enum error!";
    return 1;
}

bool Camera::Open_Camera()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    GX_OPEN_PARAM stOpenParam;
    stOpenParam.accessMode = GX_ACCESS_EXCLUSIVE;
    stOpenParam.openMode = GX_OPEN_INDEX;
    stOpenParam.pszContent = DEVICE_INDEX_TO_OPEN;
    status = GXOpenDevice(&stOpenParam, &cam_handle);
    if(status == GX_STATUS_SUCCESS)
    {
        qDebug() << ">>>>>open camera success!>>>>>";
        return true;
    }
    else
        return false;
}

bool Camera::Close_Camera()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    status = GXUnregisterCaptureCallback(cam_handle);  //注销采集回调函数
    if(status != GX_STATUS_SUCCESS)
        qDebug() << "unregister failed!!!" << endl;
    status =GXCloseDevice(cam_handle);
    if(status == GX_STATUS_SUCCESS)
    {
        qDebug() << ">>>>>close camera success>>>>>";
        GXCloseLib();   //注销库
        return true;
    }
    else
        return false;
}

bool Camera::Init_Camera()
{
    uint32_t nDeviceNum = Enum_Camera();
    if(nDeviceNum == 0)
    {
        qDebug() << ">>>>>init failed!>>>>>";
        return false;
    }
    if(Open_Camera())
    {
        qDebug() << ">>>>>init success!>>>>>";
        return true;
    }
    qDebug() << ">>>>>init failed!>>>>>";
    return false;
}

bool Camera::Register_Callback_Fun()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    status = GXRegisterCaptureCallback(cam_handle, nullptr, OnFrameCallbackFun);
    if(status == GX_STATUS_SUCCESS)
        return true;
    else
        return false;
}

bool Camera::Unregister_Callback_Fun()
{
    GX_STATUS status = GX_STATUS_SUCCESS;
    status = GXUnregisterCaptureCallback(cam_handle);
    if(status == GX_STATUS_SUCCESS)
        return true;
    else
        return false;
}

在初始化相机的时候可以设置相机采集图像的大小

 GXSetInt(my_cam->cam_handle, GX_INT_WIDTH, 1024);
 GXSetInt(my_cam->cam_handle, GX_INT_HEIGHT, 768);

设置相机采集模式及触发模式,这里设置为连续采集且无触发,一旦发送开采命令,相机将连续采图,直到发送停采命令。

GX_STATUS status = GX_STATUS_SUCCESS;
status = GXSetEnum(my_cam->cam_handle, GX_ENUM_ACQUISITION_MODE,GX_ACQ_MODE_CONTINUOUS);
if(GX_STATUS_SUCCESS != status)
	qDebug() << "set acquisition mode failed..." << status << endl;
status = GXSetEnum(my_cam->cam_handle, GX_ENUM_TRIGGER_MODE, GX_TRIGGER_MODE_OFF);
if(GX_STATUS_SUCCESS != status)
    qDebug() << "set trigger mode failed...." << status << endl;

至此相机参数配置完成,初始化完成。

2.2 相机采集回调函数

相机触发后进入回调函数,用户可以在这里完成不同需求。本例展示在回调函数中接收相机内存的BAYER图,通过DxRaw8toRGB24函数转换成RGB图,再转换到opencv::Mat类型的图像数据,在工作线程中通过cv::imshow显示图像。由于是连续触发,所以图片帧连续进入回调函数,我们在界面所看到的其实是视频流的形式。
因为回调函数频繁地进入,所以我将显示图片过程放在下面2.3节的工作线程中,防止在回调函数中imshow函数调用花费大量时间导致图片帧卡死。回调函数里只做相机内存和计算机内存间图像的拷贝工作。

#include "thread.h"
#include <QDebug>
#include "widget.h"

int64_t nPayLoadSize;
unsigned char *pRGB24Buf;
unsigned char *pRawBuf;
cv::Mat img_buff;
cv::Mat img;
int count = 0;

/*图像回调处理函数*/
void GX_STDC OnFrameCallbackFun(GX_FRAME_CALLBACK_PARAM* pFrame)
{
    if(pFrame->status == GX_FRAME_STATUS_SUCCESS)
    {
        qDebug() << ">>>>>entered the callback fun!>>>>>";
        /*打印图片长宽信息*/
//        qDebug() << "width: " << pFrame->nWidth << "height" << pFrame->nHeight;
        memcpy(pRawBuf, pFrame->pImgBuf, pFrame->nImgSize);
        DxRaw8toRGB24(pRawBuf, pRGB24Buf, pFrame->nWidth,pFrame->nHeight, 		
        			  RAW2RGB_NEIGHBOUR,BAYERRG, false);
        memcpy(img.data, pRGB24Buf, 1024*768*3);  //img为cv::Mat格式
        qDebug() << "frame: " << count++;  //显示已拍摄的帧数
    }
    return;
}

全局变量 *pRGB24Buf*pRawBuf的初始化见2.3节工作线程中。

2.3 编写采集线程

建立thread.h头文件,创建工作线程类,以及定义回调函数(回调函数中也是开启一个单独的线程进行处理,故这里将它算在线程文件中)

#include "thread.h"
#include <QDebug>
#include "widget.h"

int64_t nPayLoadSize;
unsigned char *pRGB24Buf;
unsigned char *pRawBuf;

cv::Mat img_buff;
cv::Mat img;
int count = 0;

Start_Work::Start_Work(QObject *parent) : QThread (parent)
{
    my_cam = new Camera();
}

Start_Work::~Start_Work()
{
    if(my_cam != nullptr)
        delete my_cam;
}

void Start_Work::run()
{
    bool IsOk = false;
    IsOk = my_cam->Init_Camera();
    if(!IsOk)
        qDebug() << "init camera failed" << endl;
    GX_STATUS status = GX_STATUS_SUCCESS;
    GXSetInt(my_cam->cam_handle, GX_INT_WIDTH, 1024);
    GXSetInt(my_cam->cam_handle, GX_INT_HEIGHT, 768);
    GXGetInt(my_cam->cam_handle, GX_INT_PAYLOAD_SIZE, &nPayLoadSize);
    
    pRawBuf = new unsigned char[nPayLoadSize];
    pRGB24Buf = new unsigned char[1024*768*3];
    img.create(768, 1024, CV_8UC3);  //创建cv::Mat

    IsOk = my_cam->Register_Callback_Fun();
    if(!IsOk)
        qDebug() << "register failed!" << endl;
    /*发送采集命令*/
    status = GXSendCommand(my_cam->cam_handle, GX_COMMAND_ACQUISITION_START);
    if(GX_STATUS_SUCCESS != status)
        qDebug() << "send command error..." << endl;
    qDebug() << "start_work thread going...." << endl;
    /*连续显示300帧图片,然后退出循环,发送停采命令*/
    while(1)
    {
        cv::imshow("test", img);
        if(count == 300)
            break;
    }
    status = GXSendCommand(my_cam->cam_handle, GX_COMMAND_ACQUISITION_STOP);
    if(status == GX_STATUS_SUCCESS)
    {
        IsOk = my_cam->Close_Camera();
        if(!IsOk)
            qDebug() << "close camera error!!!!" << endl;
        else
            qDebug() << "close camera success" << endl;
    }
    /*释放内存!*/
    if(pRawBuf != nullptr)
    {
        delete [] pRawBuf;
        pRawBuf = nullptr;
    }
    if(pRGB24Buf != nullptr)
    {
        delete [] pRGB24Buf;
        pRGB24Buf = nullptr;
    }
}

进入300次回调函数,显示300帧图片,测试成功。
在这里插入图片描述
此时cv::Mat格式的图片也采集到,可以结合要求进行处理

2.4 QLabel显示图片

使用QT开发时肯定不希望用opencv的imshow来显示,将图片显示在UI界面才是正确的。接下来就来完成这件事。首先在widget.ui放置一个QLabel,然后在widget.h和widget.cpp中申明定义slot槽函数:showimage(cv::Mat),参数是cv::Mat变量。这个函数负责在收到emit信号后在QLabel对象pic_show上显示图片。

void Widget::showimage(cv::Mat img)
{
    cv::cvtColor(img, img, CV_BGRA2RGB);
    const unsigned char *pSrc = (const unsigned char*)img.data;
    QImage image(pSrc, img.cols, img.rows, img.step, QImage::Format_RGB888);
    pix = QPixmap::fromImage(image.scaled(ui->pic_show->width(),ui->pic_show->height(),Qt::KeepAspectRatio));
    ui->pic_show->setPixmap(pix);
    ui->pic_show->show();
}

在thread.h的Start_Work类中申明信号send_image(cv::Mat)

signals:
    void send_image(cv::Mat);

同时在widget类的构造函数中绑定信号和槽

connect(start_thread, SIGNAL(send_image(cv::Mat)), this, SLOT(showimage(cv::Mat)),Qt::BlockingQueuedConnection);

最后一步替换2.3节中Start_Work::run()中while(1)中的内容,在循环中emit发送信号即可

 while(1)
 	emit send_image(img);

编译运行,调试结果如下
在这里插入图片描述

总结

以上就是关于大恒相机的C语言SDK下的初步开发流程,我们完成了采集图片,抓取图片,转换为Opencv::Mat格式,显示图片的功能。Mat格式的图片方便利用Opencv对图像进行后续处理。
除此之外,在后续开发过程中,要注意多线程开发中对全局图片变量的加锁处理,以防止多个线程对资源的争夺,可以采用信号量的方法建立图片缓存区,保证线程之间不冲突。

标签:status,STATUS,10GC,1070,GX,相机,Camera,cam,MER
来源: https://blog.csdn.net/qq_40734815/article/details/121389270

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有