Qt调用工业相机之巴斯勒相机

作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

开发环境

操作系统:Windows 10 1903 18362.778
相机型号:BASLER acA 1300-60gm
相机软件:pylon_Runtime_6.0.0.17906,Basler_pylon_5.0.10.10613
软件版本:Qt 5.12.8, vs2017 Community
通信方式:GigE - 千兆以太网通信协议

驱动与SDK

开发包和驱动下载地址
提取码:lg0i

驱动安装完成之后需要完成一些配置才能成功连接相机

  1. 将相机通电,使用网线将相机与电脑连接。确保相机与电脑处于连接状态。可以观察网
    络连接中,本地连接的状态;
    Qt调用工业相机之巴斯勒相机_第1张图片
  2. 网络连接中,右键本地连接,选择属性。在属性界面中点击”配置”按钮,进入”高级”选项卡,设置 Jumbo Packet (巨型帧)为当前可设置的最大值。点击确定保存参数;
    Qt调用工业相机之巴斯勒相机_第2张图片
    Qt调用工业相机之巴斯勒相机_第3张图片
  3. 在网络连接中,右键”本地连接”,双击 “Internet 协议版本 4”,在弹出界面中勾选 “自动获得 IP 地址”
    ,点击确定保存参数;
    Qt调用工业相机之巴斯勒相机_第4张图片
    Qt调用工业相机之巴斯勒相机_第5张图片
  4. 打开 “Pylon IP Configurator”工具,在列表中选择当前连接的相机。选择相机后,选择 “DHCP”,点击保存。”Refresh” 确保相机的 “Status” 为 ”OK”,并且界面右下角没有警告;
    Qt调用工业相机之巴斯勒相机_第6张图片

安装驱动和开发包完成之后,可以找到以下目录:

  1. C:\Program Files\Basler\pylon 5
    Qt调用工业相机之巴斯勒相机_第7张图片
  • Applications - 应用程序 pylon Viewer 和 pylon IP Configurator;
  • Development - 开发文档、头文件、库文件、示例程序源代码等等;
  • License - 开发许可证;
  • Runtime - 运行时需要的依赖库(打包程序时需要包含);
  • Release Notes.txt - 发行笔记;

巴斯勒相机介绍

巴斯勒相机实时获取图像的原理就是对每一帧数据的处理,即 拍照 - 处理数据 - 拍照 - 处理数据 …
SDK中提供了两种方式来处理图像,我们先来看一下最主要的开始抓取图像的函数 StartGrabbing:
virtual void StartGrabbing( EGrabStrategy strategy = GrabStrategy_OneByOne,
EGrabLoop grabLoopType = GrabLoop_ProvidedByUser);

来看一下函数的两个参数代表什么含义

  • One by One Grab Strategy
    这种模式是最简单的,也是 CInstantCamera 默认的图像获取策略。
    获取到的图像放到一个FIFO 队列中。每次我们从队列中取的(RetrieveResult)都是最早放到队列中的图像

  • Latest Image Only Grab Strategy
    这个策略下只有最新到的图像保存在输出队列中。
    如果一份旧图像还没被读取新图像就又来了,那么旧图像就被舍弃。这样可以保证读取到图像永远都是读取时刻最新的。

  • Latest Images Grab Strategy
    这个模式下输出队列可以保存不止一张图像,具体最多能保存几张图象由 CInstantCamera::OutputQueueSize 来指定。
    当新的图像来了,而队列已满时,旧的图像被舍弃。
    当 CInstantCamera::OutputQueueSize 为 1 时就是 Latest Image Only Grab Strategy。
    当 CInstantCamera::OutputQueueSize = CInstantCamera::MaxNumBuffer,就和 One by One Grab Strategy 一样了。

  • Upcoming Image Grab Strategy
    这个模式很特别,只有当 CInstantCamera::RetrieveResult() 被调用后才会采集图像。USB 相机不支持这种策略。

  • GrabLoop_ProvidedByUser
    用户在代码中循环调用RetrieveResult() 来处理抓取的图像和相机事件。默认使用这种模式

  • GrabLoop_ProvidedByInstantCamera
    抓取循环线程由即时相机提供。它会不断循环调用RetrieveResult()。抓取的图像由注册的图像事件处理程序处理。抓取循环线程在抓取启动时启动

StartGrabbing的第一个参数代表获取图像的策略,第二个参数代表的是使用哪个循环线程来抓取图像。由第二个参数我们就可以分两种方式来处理图像数据:

  1. 用户自己处理,步骤如下:
  • 调用 StartGrabbing 开始抓取图像
    m_baslerCamera.StartGrabbing(GrabStrategy_OneByOne, GrabLoop_ProvidedByUser);
  • 用户自己循环调用 RetrieveResult
    m_baslerCamera.RetrieveResult(1000, ptrGrabResult, TimeoutHandling_ThrowException);
  • 处理数据 ptrGrabResult
    CImagePersistence::Save(ImageFileFormat_Bmp, filename, ptrGrabResult);
  1. 使用相机的循化线程,步骤如下:
  • 注册图像事件处理程序
    m_baslerCamera.RegisterImageEventHandler(this, RegistrationMode_Append, Cleanup_Delete);
  • 重写图像处理函数 OnImageGrabbed
    virtual void OnImageGrabbed(CInstantCamera &camera, const CGrabResultPtr &grabResult) override;
  • 调用 StartGrabbing 开始抓取图像
    m_baslerCamera.StartGrabbing(GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera);
  • 注意事项:相机获取到图像就会调用回调函数 OnImageGrabbed,图像处理是在不同的线程所以需要加锁

我的代码

实现功能:相机图像的实时显示,并且可以在需要的时候获取当前帧数据,用于分析或者保存;

首先需要在pro中配置头文件和库文件

INCLUDEPATH += $$PWD/include/ \
               $$PWD/include/pylon

windows {
    contains(DEFINES, WIN64) {
        LIBS += -L$$PWD/lib/x64/ -lGCBase_MD_VC120_v3_0_Basler_pylon_v5_0
        LIBS += -L$$PWD/lib/x64/ -lGenApi_MD_VC120_v3_0_Basler_pylon_v5_0
        LIBS += -L$$PWD/lib/x64/ -lPylonBase_MD_VC120_v5_0
        LIBS += -L$$PWD/lib/x64/ -lPylonC_MD_VC120
        LIBS += -L$$PWD/lib/x64/ -lPylonGUI_MD_VC120_v5_0
        LIBS += -L$$PWD/lib/x64/ -lPylonUtility_MD_VC120_v5_0
    } else {
        LIBS += -L$$PWD/lib/Win32/ -lGCBase_MD_VC120_v3_0_Basler_pylon_v5_0
        LIBS += -L$$PWD/lib/Win32/ -lGenApi_MD_VC120_v3_0_Basler_pylon_v5_0
        LIBS += -L$$PWD/lib/Win32/ -lPylonBase_MD_VC120_v5_0
        LIBS += -L$$PWD/lib/Win32/ -lPylonC_MD_VC120
        LIBS += -L$$PWD/lib/Win32/ -lPylonGUI_MD_VC120_v5_0
        LIBS += -L$$PWD/lib/Win32/ -lPylonUtility_MD_VC120_v5_0
    }
}

自定义相机基类 Camera

#ifndef CAMERA_H
#define CAMERA_H

#include 
#include 
#include 
#include 

#include 

class Camera : public QObject
{
    Q_OBJECT

public:
    enum CameraType {
        Basler = 1,                     // 巴斯勒相机
        IC_Imaging,                     // 映美精相机
        MV,                             // 海康威视相机
        Virtual                         // 虚拟相机
    };

    explicit Camera(CameraType type = Basler) : m_type(type) {}

    virtual void initCamera() = 0;      // 初始化相机
    virtual void destroyCamera() = 0;   // 销毁相机
    virtual void openCamera() = 0;      // 打开相机
    virtual void closeCamera() = 0;     // 关闭相机
    virtual void startWork() = 0;       // 开始工作
    virtual void stopWork() = 0;        // 停止工作
    virtual cv::Mat takeAPic() = 0;     // 获取当前图像

    void start() { m_timer.start(); }
    void stop() { m_timer.stop(); }
    void setInterval(int time) { m_timer.setInterval(time); }
    CameraType getCameraType() { return m_type; }

signals:
    void updateImage(QImage image);

protected:
    CameraType m_type;
    QMutex m_mutex;
    QTimer m_timer;
};

#endif // CAMERA_H

自定义 BBaslerCamerControl 相机控制类

#ifndef BBASLERCAMERCONTROL_H
#define BBASLERCAMERCONTROL_H

#include "../camera.h"
#include "PylonIncludes.h"

using namespace std;
using namespace Pylon;
using namespace GenApi;

class BBaslerCamerControl : public Camera
{
    Q_OBJECT
public:
    enum BaslerCameraProperty {
        DeviceModelName,            // 相机名称
        DeviceID,                   // 相机SN号
        ResultingFrameRateAbs,      // 相机帧率
        AcquisitionFrameRateAbs,    // 相机频率
        Freerun,                    // 相机内触发
        Line1,                      // 相机外触发
        ExposureTimeAbs,            // 相机曝光时间
        GainRaw,                    // 相机增益
        SensorWidth,                // 传感器宽度
        SensorHeight,               // 传感器高度
        Width,                      // 图片宽度
        Height,                     // 图片高度
        PixelFormat,                // 图片的格式
    };

    explicit BBaslerCamerControl(Camera::CameraType type = Camera::CameraType::Basler);

    virtual void initCamera() override;
    virtual void destroyCamera() override;

    virtual void openCamera() override;
    virtual void closeCamera() override;

    virtual void startWork() override;
    virtual void stopWork() override;

    virtual cv::Mat takeAPic() override;

    QString getCameraProperty(BBaslerCamerControl::BaslerCameraProperty type);                  // 获取相机参数
    void setCameraProperty(BBaslerCamerControl::BaslerCameraProperty type, double value = 0.0); // 设置相机参数

public slots:
    void updateFrame();

private:
    CInstantCamera m_baslerCamera;  // 实例化相机对象
    INodeMap *m_nodeMap;            // 相机属性节点
    QString m_currentMode;          // 相机触发模式
};

#endif // BBASLERCAMERCONTROL_H
#include "bbaslercamercontrol.h"
#include "globalfun.h"

BBaslerCamerControl::BBaslerCamerControl(Camera::CameraType type) : Camera(type)
{
    m_timer.setInterval(GlobalValue::cam_itl);
    connect(&m_timer, &QTimer::timeout, this, &BBaslerCamerControl::updateFrame);
}

void BBaslerCamerControl::initCamera()
{
    try {
        Pylon::PylonInitialize();   // 调用其他 pylon 函数之前必须调用 PylonInitialize 完成初始化
    } catch (GenICam::GenericException &e) {
        qDebug() << "initCamera erroer: " + QString::fromLocal8Bit(e.what());
    }
}

void BBaslerCamerControl::destroyCamera()
{
    try {
        stopWork();
        closeCamera();

        Pylon::PylonTerminate();    // 释放 pylon 运行时系统分配的资源
    } catch (GenICam::GenericException &e) {
        qDebug() << "destroyCamera erroer: " + QString::fromLocal8Bit(e.what());
    }
}

void BBaslerCamerControl::openCamera()
{
    try {
        m_baslerCamera.Attach(CTlFactory::GetInstance().CreateFirstDevice());   //实例化找到的第一个相机
        m_baslerCamera.Open();      // 打开相机

        m_nodeMap = &m_baslerCamera.GetNodeMap();   // 获取相机属性节点
        m_currentMode = getCameraProperty(Line1);   // 获取相机触发模式
    } catch (GenICam::GenericException &e) {
        qDebug() << "openCamera erroer: " + QString::fromLocal8Bit(e.what());
    }
}

void BBaslerCamerControl::closeCamera()
{
    try {
        if ( m_baslerCamera.IsOpen() ) {
            m_baslerCamera.DetachDevice();  // 分离连接的 pylon 设备
            m_baslerCamera.Close();         // 关闭连接的 pylon 设备

            m_nodeMap = nullptr;            // 相机属性节点置空
            m_currentMode = "";             // 相机触发模式置空
        }

    } catch (GenICam::GenericException &e) {
        qDebug() << "closeCamera erroer: " + QString::fromLocal8Bit(e.what());
    }
}

void BBaslerCamerControl::startWork()
{
    // Check if camera is open.
    if ( !m_baslerCamera.IsOpen() ) {
        return;
    }

    try {
        m_baslerCamera.StartGrabbing(GrabStrategy_LatestImageOnly, GrabLoop_ProvidedByUser);  // 开始抓取图像
        m_timer.start();
    } catch (GenICam::GenericException &e) {
        qDebug() << "startWork erroer: " + QString::fromLocal8Bit(e.what());
    }
}

void BBaslerCamerControl::stopWork()
{
    try {
        if ( m_baslerCamera.IsGrabbing() ) {
            m_baslerCamera.StopGrabbing();  // 停止抓取图像
            m_timer.stop();
        }
    } catch (GenICam::GenericException &e) {
        qDebug() << "stopWork erroer: " + QString::fromLocal8Bit(e.what());
    }
}

cv::Mat BBaslerCamerControl::takeAPic()
{
    // Check if camera is open.
    if ( !m_baslerCamera.IsOpen() ) {
        return cv::Mat();
    }

    QMutexLocker locker(&m_mutex);

    try {
        CGrabResultPtr ptrGrabResult;
        m_baslerCamera.RetrieveResult(1000, ptrGrabResult, TimeoutHandling_ThrowException);

        if ( ptrGrabResult->GrabSucceeded() ) {
            CPylonImage pylogimage;
            CImageFormatConverter formatconverter;
            formatconverter.OutputPixelFormat = PixelType_RGB8packed;
            formatconverter.Convert(pylogimage, ptrGrabResult);

            cv::Mat mat = cv::Mat(int( ptrGrabResult->GetHeight() ),
                                  int( ptrGrabResult->GetWidth() ),
                                  CV_8UC3,
                                  static_cast< uchar* >( pylogimage.GetBuffer() ));

            return mat.clone();
        } else {
            return cv::Mat();
        }
    } catch (GenICam::GenericException &e) {
        qDebug() << "takeAPic erroer: " + QString::fromLocal8Bit(e.what());
        return cv::Mat();
    }
}

QString BBaslerCamerControl::getCameraProperty(BBaslerCamerControl::BaslerCameraProperty type)
{
    // Check if camera is open.
    if ( !m_baslerCamera.IsOpen() ) {
        return "";
    }

    QString ret = "";

    try {
        switch (type) {
        case DeviceModelName: {
            const CStringPtr deviceModelName = m_nodeMap->GetNode("DeviceModelName");
            ret = QString::fromLocal8Bit(deviceModelName->ToString().c_str());
        } break;
        case DeviceID: {
            const CStringPtr deviceID = m_nodeMap->GetNode("DeviceID");
            ret = QString::fromLocal8Bit(deviceID->ToString().c_str());
        } break;
        case ResultingFrameRateAbs: {
            const CFloatPtr resultingFrameRateAbs = m_nodeMap->GetNode("ResultingFrameRateAbs");
            ret = QString::number(resultingFrameRateAbs->GetValue());
        } break;
        case AcquisitionFrameRateAbs: {
            const CBooleanPtr acquisitionFrameRateEnable = m_nodeMap->GetNode("AcquisitionFrameRateEnable");
            acquisitionFrameRateEnable->SetValue(TRUE);
            const CFloatPtr acquisitionFrameRateAbs = m_nodeMap->GetNode("AcquisitionFrameRateAbs");
            ret = QString::number(acquisitionFrameRateAbs->GetValue());
        } break;
        case Freerun:
        case Line1: {
            const CEnumerationPtr triggerSelector = m_nodeMap->GetNode("TriggerSelector");
            triggerSelector->FromString("FrameStart");
            CEnumerationPtr triggerSource = m_nodeMap->GetNode("TriggerSource");
            ret = QString::fromLocal8Bit(triggerSource->ToString().c_str());
        } break;
        case ExposureTimeAbs: {
            const CFloatPtr exposureTimeAbs = m_nodeMap->GetNode("ExposureTimeAbs");
            ret = QString::number(exposureTimeAbs->GetValue());
        } break;
        case GainRaw: {
            const CIntegerPtr gainRaw = m_nodeMap->GetNode("GainRaw");
            ret = QString::number(gainRaw->GetValue());
        } break;
        case SensorWidth: {
            const CIntegerPtr sensorWidth = m_nodeMap->GetNode("SensorWidth");
            ret = QString::number(sensorWidth->GetValue());
        } break;
        case SensorHeight: {
            const CIntegerPtr sensorHeight = m_nodeMap->GetNode("SensorHeight");
            ret = QString::number(sensorHeight->GetValue());
        } break;
        case Width: {
            const CIntegerPtr width = m_nodeMap->GetNode("Width");
            ret = QString::number(width->GetValue());
        } break;
        case Height: {
            const CIntegerPtr height = m_nodeMap->GetNode("Height");
            ret = QString::number(height->GetValue());
        } break;
        case PixelFormat: {
            const CEnumerationPtr pixelFormat = m_nodeMap->GetNode("PixelFormat");
            ret = QString::fromLocal8Bit(pixelFormat->ToString().c_str());
        } break;
        default: ret = ""; break;
        }
    } catch (GenICam::GenericException &e) {
        qDebug() << "getCameraProperty erroer: " + QString::fromLocal8Bit(e.what());
    }

    return ret;
}

void BBaslerCamerControl::setCameraProperty(BBaslerCamerControl::BaslerCameraProperty type, double value)
{
    // Check if camera is open.
    if ( !m_baslerCamera.IsOpen() ) {
        return;
    }

    try {
        switch (type) {
        case AcquisitionFrameRateAbs: {
            const CBooleanPtr acquisitionFrameRateEnable = m_nodeMap->GetNode("AcquisitionFrameRateEnable");
            acquisitionFrameRateEnable->SetValue(TRUE);
            const CFloatPtr acquisitionFrameRateAbs = m_nodeMap->GetNode("AcquisitionFrameRateAbs");
            acquisitionFrameRateAbs->SetValue(value);
        } break;
        case Freerun: {
            CEnumerationPtr triggerSelector = m_nodeMap->GetNode("TriggerSelector");
            triggerSelector->FromString("FrameStart");
            CEnumerationPtr triggerMode = m_nodeMap->GetNode("TriggerMode");
            triggerMode->SetIntValue(1);
            CEnumerationPtr triggerSource = m_nodeMap->GetNode("TriggerSource");
            triggerSource->FromString("Software");
        } break;
        case Line1: {
            CEnumerationPtr triggerSelector = m_nodeMap->GetNode("TriggerSelector");
            triggerSelector->FromString("FrameStart");
            CEnumerationPtr triggerMode = m_nodeMap->GetNode("TriggerMode");
            triggerMode->SetIntValue(1);
            CEnumerationPtr triggerSource = m_nodeMap->GetNode("TriggerSource");
            triggerSource->FromString("Line1");
        } break;
        case ExposureTimeAbs: {
            const CFloatPtr exposureTimeAbs = m_nodeMap->GetNode("ExposureTimeAbs");
            exposureTimeAbs->SetValue(value);
        } break;
        case GainRaw: {
            const CIntegerPtr gainRaw = m_nodeMap->GetNode("GainRaw");
            gainRaw->SetValue(value);
        } break;
        case Width: {
            const CIntegerPtr width = m_nodeMap->GetNode("Width");
            width->SetValue(value);
        } break;
        case Height: {
            const CIntegerPtr height = m_nodeMap->GetNode("Height");
            height->SetValue(value);
        } break;
        default: break;
        }
    } catch (GenICam::GenericException &e) {
        qDebug() << "setCameraProperty erroer: " + QString::fromLocal8Bit(e.what());
    }
}

void BBaslerCamerControl::updateFrame()
{
    QMutexLocker locker(&m_mutex);

    try {
        CGrabResultPtr ptrGrabResult;
        m_baslerCamera.RetrieveResult(1000, ptrGrabResult, TimeoutHandling_ThrowException);

        if ( ptrGrabResult->GrabSucceeded() ) {
            CPylonImage pylogimage;
            CImageFormatConverter formatconverter;
            formatconverter.OutputPixelFormat = PixelType_RGB8packed;
            formatconverter.Convert(pylogimage, ptrGrabResult);

            cv::Mat mat = cv::Mat(int( ptrGrabResult->GetHeight() ),
                                  int( ptrGrabResult->GetWidth() ),
                                  CV_8UC3,
                                  static_cast< uchar* >( pylogimage.GetBuffer() ));

            QImage image((const unsigned char *)(mat.data), mat.cols, mat.rows, mat.cols * 3, QImage::Format_RGB888);
            emit updateImage(image.rgbSwapped());
        }
    } catch (GenICam::GenericException &e) {
        qDebug() << "updateFrame erroer: " + QString::fromLocal8Bit(e.what());
    }
}

其他请参考

  • Qt调用工业相机之映美精相机
  • Qt调用工业相机之海康威视相机

你可能感兴趣的:(Qt)