天天看点

Basler Blaze-101开发实践(1)——实时采图前言效果图环境安装正文总结

目录

  • 前言
  • 效果图
  • 环境安装
    • SDK下载
  • 正文
    • 相机的初始化
    • 实时采图
      • 开始抓图
      • 抓图函数
      • 停止抓图
  • 总结

前言

关于Basler的相机还是很常见的,网上也有很多代码可以参考,但这个相机的版本是Blaze101。相信看到这篇文章的应该都知道,关于这个相机的资料确实很少,所以,我这里也是完成了这个相机的实时采图、触发采图、参数设置,三种功能。这里把一些资料也都发出来,希望能对看到这篇文章的人有所帮助。

效果图

Basler Blaze-101开发实践(1)——实时采图前言效果图环境安装正文总结

上面这个效果图都是在相机没有加光源以及没有加镜头的情况下进行采集的。这里的触发采集会比较糊,我也不知道为啥,我自己录起来是没事的,但是放在CSDN就这样了。反正图像也还行。

环境安装

SDK下载

这里直接给出下载路径:Basler Blaze101sdk下载链接,这里注意,你进去官网,自己找的话,如果是关于这个相机的话,你要把页面调整成英文模式,才会出现这个相机SDK。

里面的那些库在软件的安装目录之下,这个自己去找下就行。

使用的lib文件是:

Basler Blaze-101开发实践(1)——实时采图前言效果图环境安装正文总结

这些lib文件所需要依赖的dll文件为(也就是运行时所需要依赖的dll文件,注意要将这些文件放在你程序的运行目录之下):

Basler Blaze-101开发实践(1)——实时采图前言效果图环境安装正文总结

正文

相机的初始化

a. 先给出这部分函数的代码:

bool MDeviceBlaze3d::InitDevice(QString index,QString identifier)
{
    m_cCameraID = index;
    m_cIdentifier = identifier;
    bool ret = false;
    m_iFinishAcqFlag = false;//标志AcquisitionStop是否执行的标志
    m_iGrabStopFlag = false;//标志GrabThreadStop是否执行的标志
    m_iGrabThreadStopNumber = 0;//标志GrabThreadStop是在第几次执行的数量标识
    m_iAcqStartNumber = 0;//标志AcquisitionStart是在第几次执行的数量标识
//上面这几个标识在后面进行主动操作与触发操作进行切换时,为保证功能切换完美,所需进行的操作。
#ifdef WIN_BLAZE_3D
    if(DeviceLoading(m_cIdentifier))
    {
        m_pConfigureObj = new MConfigureBlaze3d();//配置参数的对象初始化
        m_pConfigureObj->InitConfigure(m_cCameraID,(MDeviceAbstract*)this);
        m_pConfigureObj->RegisterConfigureCallback(m_fXmlChange,m_pCallUser);

        QVariant wVal;
        QVariant hVal;
        QVariant pixelForamt;
        QVariant frameRate;
        this->GetParameter("Width",wVal);
        this->GetParameter("Height",hVal);
        this->GetParameter("PixelFormat",pixelForamt);
        this->GetParameter("AcquisitionFrameRate",frameRate);
        m_iWidth = wVal.toInt();//这个m_iWidth以及m_iHeight对图像后面的显示很有用,如果你先做的实时采图,那这两个值应该先赋值
        m_iHeight = hVal.toInt();
        m_cPixelFormat = pixelForamt.toString();
        m_fFrameRate = frameRate.toFloat();
        m_pGralObj = new MGrabBlaze3d();
        if(m_pGralObj)
        {
            m_pGralObj->InitGrab(index,(MDeviceAbstract*)this);//这个是对抓图对象的初始化
            m_pGralObj->SetPixelFormat(m_cPixelFormat);
        }
        //m_pGralObj->RegisterCallBackFunEx(m_pCallUser,m_fGrabCallbackEx);
        ret = true;
    }
#endif
    return ret;
}
           

这部分是对一些类的对象入配置参数的对象以及采集抓图的函数进行初始化,以及对许多变量进行初始化。

  1. 下面这个是一些通过

    EnumerateCameras

    从而得到相机的许多信息,例如序列号,用户号,等等。先给出整个函数:
bool MDeviceBlaze3d::DeviceLoading(QString identifier)
{
    m_bLoaded = false;
#ifdef WIN_BLAZE_3D
    try
    {
        //CameraList list = CGenTLCamera<BlazeProducerName>::EnumerateCameras();
        CameraList list = CBlazeCamera::EnumerateCameras();//这个是获取相机信息的语句
        qDebug()<<"DeviceLoading"<<list.size();
        for(auto it = list.begin();it!=list.end();it++)
        {
            CameraInfo info = *it;
            if((!identifier.isEmpty()) &&(info.IsValid()))
            {
                QString defName = QString::fromStdString(info.strUserDefinedName);
                QString serialNum = QString::fromStdString(info.strSerialNumber);
                if(identifier.contains(serialNum))
                {
                    m_camereInstant.Open(info);
                    if(m_camereInstant.IsOpen())
                    {
                        GenApi::CEnumerationPtr ptrComponentSelector = m_camereInstant.GetParameter("ComponentSelector");
                        ptrComponentSelector->FromString("Range");
                        GenApi::CBooleanPtr ptrComponentEnable = m_camereInstant.GetParameter("ComponentEnable");
                        ptrComponentEnable->SetValue(true);
                        GenApi::CEnumerationPtr ptrPixelFormat = m_camereInstant.GetParameter("PixelFormat");
                        ptrPixelFormat->FromString("Coord3D_ABC32f");
                        m_bLoaded = true;
                        //以下就是这些info带来的相应的信息
                        m_cModelName = QString::fromStdString(info.strModelName);
                        m_cSerialNumber = QString::fromStdString(info.strSerialNumber);
                        m_cCameraVendor = QString::fromStdString("Basler");
                        m_cUserDefine = QString::fromStdString(info.strUserDefinedName);
                        m_cDeviceVersion = QString::fromStdString(info.strDeviceID);
                    }
                    break;
                }
                else if((!defName.isEmpty()) && identifier.contains(defName))
                {
                    m_camereInstant.Open(info);
                    if(m_camereInstant.IsOpen())
                    {
                        m_bLoaded = true;
                        m_cModelName = QString::fromStdString(info.strModelName);
                        m_cSerialNumber = QString::fromStdString(info.strSerialNumber);
                        m_cCameraVendor = QString::fromStdString("Basler");
                        m_cUserDefine = QString::fromStdString(info.strUserDefinedName);
                        m_cDeviceVersion = QString::fromStdString(info.strDeviceID);
                    }
                    break;
                }
            }
        }
        qDebug()<<"DeviceLoading"<<m_bLoaded;
    }
    catch(GenICam::GenericException& ex)
    {
        qDebug()<<"-->Error:"<<ex.GetDescription();
        m_bLoaded = false;
    }
#endif
    return m_bLoaded;
}
           

你可以在这一部分进行一些相机最开始需要进行初始化函数的放置。

实时采图

接下来就是主菜了,基本从这几个函数,就可以实现相机的实时采图了。

开始抓图

首先,肯定是你要开启抓图,当然这个肯定是排列在你初始化后的。先给出对应的这个函数。

qint32  MDeviceBlaze3d::AcquisitionStart()
{
    qint32 ret = RETURN_FAIL;
#ifdef WIN_BLAZE_3D
    QVariant triggerMode;
    this->GetParameter("TriggerMode",triggerMode);
    QString triggerMode2 = triggerMode.toString();

    try
    {
        if(m_bLoaded)
        {
            if(m_bStopWork)
            {
                m_bStopWork =false;
                m_iAcqStartNumber++;
				//下面的这三个if都是为了主动抓图与触发抓图进行完美切换所做的一些操作,
				//接下来的文章如果讲到触发采图的话,就会讲这部分的内容,所以不用急。
                if((triggerMode2=="Off"&&m_iFinishAcqFlag))
                {
                    m_camereInstant.FinishAcquisition();
                }

                if(triggerMode2=="Off"&&m_iFinishAcqFlag)
                {
                    m_camereInstant.PrepareAcquisition(nBuffers);
                    m_camereInstant.QueueBuffer(idxNextBuffer);
                }

                if(triggerMode2=="Off"&&m_iGrabStopFlag&&m_iAcqStartNumber==2)
                {
                    m_camereInstant.FinishAcquisition();
                }

                m_camereInstant.StartAcquisition();
                m_camereInstant.IssueAcquisitionStartCommand();

                if(m_pConfigureObj->UpdateConfigureFromDevice())
                    m_pConfigureObj->SlotConfigChanged();
                if(m_pConfigureObj->UpdateSimplyConfigureFromDevice())
                    m_pConfigureObj->SlotSimplyConfigChanged();

                ret = RETURN_OK;
            }
        }
    }
    catch(GenICam::GenericException &e)
    {
        qDebug()<< "error: AcquisitionStart"<<e.GetDescription();
    }
#endif
    return ret;
}
           

讲解:

  1. 这部分的内容重要的基本就是这一句:

这句话就可以开启抓图了,但我们这里看一下这个的源码:

void GenTLConsumerImplHelper::CGenTLCamera<ProducerName>::StartAcquisition()
{
    Internal::LockGuard lock(m_Lock);

    CIH_CHECK_INITIALIZED();
    CIH_CHECK_ACQ_PREPARED();
    using namespace GenTL;

    // Lock critical camera parameters that must not be changed while acquiring image data.
    GenApi::CIntegerPtr ptrLocked = GetParameter("TLParamsLocked");
    if (ptrLocked.IsValid() && GenApi::IsWritable(ptrLocked))
        ptrLocked->SetValue(1);

    GC_ERROR status = s_Producer.DSStartAcquisition(GetDataStreamHandle(), ACQ_START_FLAGS_DEFAULT, GENTL_INFINITE);
    if (status != GC_ERR_SUCCESS)
    {
        throw RUNTIME_EXCEPTION(s_Producer.CreateErrorMessage("Failed to start the acquisition (DSStartAcquisition failed)"));	
    }
    if (nullptr != m_ptrGrabResources.get())
        m_ptrGrabResources->SetCleanupLevel(GrabResources::StopProducer);

}
           

这里一旦开启,就会自动触发接下来要给出的这个抓图函数,所以,我有试过,就是打印一下这个

StartAcquisition

这个语句下面的看能不能执行到下面,结果是不能,具体原因未知,应该是我设置的有问题。

抓图函数

这里的抓图函数就是直接开始拿相机的数据了,这里先给出这部分的代码:

qint32 MDeviceBlaze3d::CaptureImageEx(QImage *img, MFrameInfo *info)
{
    qint32 ret = RETURN_FAIL;
#ifdef WIN_BLAZE_3D
    try
    {
        if(m_bLoaded)
        {
            BufferParts parts;
            GrabResultPtr ptrGrabResult = m_camereInstant.GrabSingleImage(1000, &parts);//拿到抓图函数的句柄,我这里使用的抓取单帧,所以我用了一个定时器来不断执行这个抓图函数,
        //不断的抓取单帧,然后把数据传到最上层进行处理。
            if ((ptrGrabResult->status == GrabResult::Ok)&&
                    (parts[1].dataFormat == PFNC_Mono16))
            {
                info->nWidth     =(qint32) parts[1].width;
                info->nHeight    =(qint32) parts[1].height;
                info->nFramerLen = parts[1].size;
                info->cFormat    = PIXEL_FROMAT_MONO16;
                info->fFrameRate = m_fFrameRate;
                QImage::Format format = QImage::Format_Grayscale16;

                //判断图片缓存是否正确
                if((img->width() !=m_iWidth) ||
                        (img->height() !=m_iHeight) ||
                        (img->format() !=format) )
                {
                    qDebug()<<"--> BaserGEV:CaptureImageEx reset image memory! ";
                    *img = QImage(m_iWidth,m_iHeight,format);//这里新new一个img用来放这个数据
                    if(format == QImage::Format_Indexed8)
                        img->setColorTable(m_vColorTabel);
                }

                uint16_t* pIntensity = (uint16_t*)parts[1].pData;//把这个数据的buffer地址的数据传到img所指向的指针

                if(img->byteCount() >= info->nFramerLen)
                    memcpy(img->bits(),pIntensity,info->nFramerLen);
                else
                    memcpy(img->bits(),pIntensity,img->byteCount());
                ret = RETURN_OK;
            }
            else
            {
                qDebug()<<"Failed to grab an image.";
            }
        }
    }
    catch(GenICam::GenericException &e)
    {
        qDebug()<< "-->Error:"<<e.GetDescription();
    }
#else
    Q_UNUSED(img)
    Q_UNUSED(info)
#endif
    return ret;
}
           

解析:

  1. 这里面最重要的当然是抓取单帧的这个函数了,那么这个抓取单帧的函数为啥这么牛,我们看一下源码就知道了。
GenTLConsumerImplHelper::GrabResultPtr GenTLConsumerImplHelper::CGenTLCamera<ProducerName>::GrabSingleImage(uint32_t timeout_ms, BufferParts* pParts)
{
    Internal::LockGuard lock(m_Lock);

    CIH_CHECK_INITIALIZED();

    CIH_CHECK_OPEN();
    using namespace GenTL;

//首先,判断是否相机已经可以开始抓图了
    if (IsGrabbing() && !IsInSingleGrabMode())
    {
        // While a continuous grab is active, single grab is not allowed.
        throw LOGICAL_ERROR_EXCEPTION("Single grab is not allowed while continuous grab operation is in progress. Stop acquisition first.");
    }

    if (pParts != nullptr)
        pParts->resize(0);
    GrabResult result;

    assert(!IsGrabbing() || IsInSingleGrabMode());

    bool first = false;
    //如果是第一次开启这个函数,那么就要进行准备资源了,PrepareAcquisition(1),这个函数后面
    //很重要,先略微记一下。
    if (!IsGrabbing())
    {
        first = true;
        PrepareAcquisition(1);

        m_isStopGrabRequested = false;
        m_State |= (stGrabbing | stSingleGrab);
        m_ptrSingleGrabFinalizer.reset(new Internal::GrabFinalizer<ProducerName>(*this));
    }

    {
        if (!m_ptrGrabResources->GetBufferPtrs()[0].isQueued())
        {

            QueueBuffer((size_t)0);
        }
        if (first)
        {

            // First start the producer
            //第一次开启这个就是进行acquisition。所以,这里我们也有道理怀疑,之前的那个StartAcquisition到底有没有必要,但事实应该是有的,我有尝试过,就采图失败了,应证了那句存在就是合理的语句。
            StartAcquisition();

            // ..then start the camera
            IssueAcquisitionStartCommand();
        }
        lock.release();
        ResetWaitObject(m_GrabStopped);

        GetGrabResult(result, timeout_ms);//注意,这个相机的特殊点就在之类,之前我们都是可以直接拿到句柄,但这个相机应该是在你如果抓到图后,调用这个函数去拿去result,然后才可以拿到对应的数据。

        SignalWaitObject(m_GrabStopped);
        lock.acquire(m_Lock);

        if (result.status == GrabResult::Ok)
        {
            GetBufferParts(result, *pParts);
        }
    }
    return GrabResultPtr(result, m_pBufferAllocator, true);
}
           
  1. 接下来是拿出我们要的一些信息:
info->nWidth     =(qint32) parts[1].width;
info->nHeight    =(qint32) parts[1].height;
info->nFramerLen = parts[1].size;
info->cFormat    = PIXEL_FROMAT_MONO16;
info->fFrameRate = m_fFrameRate;
           

至于这里为啥part[1],而不是part[0],我也不知道,但好像partp[0]我是没办法拿到比较好的数据的,所以,你可以试一下。

  1. 接下来的这个操作比较常见:
uint16_t* pIntensity = (uint16_t*)parts[1].pData;//把这个数据的buffer地址的数据传到img所指向的指针
                if(img->byteCount() >= info->nFramerLen)
                    memcpy(img->bits(),pIntensity,info->nFramerLen);
                else
                    memcpy(img->bits(),pIntensity,img->byteCount());
           

因为我们这里要把pData的数据给img,所以,肯定要考虑哪个的buf会大一点,别最后弄得,有些数据都丢失了,就尴尬了。

停止抓图

这里基本上跟前面的开始抓图比较相似,我这里设置的是当那个采集按钮再按一次就执行关闭实时采图的功能。这里给出该函数的代码:

qint32  MDeviceBlaze3d::AcquisitionStop()
{
    qint32 ret = RETURN_FAIL;
#ifdef WIN_BLAZE_3D
    QVariant triggerMode;
    this->GetParameter("TriggerMode",triggerMode);
    QString triggerMode2 = triggerMode.toString();
    try
    {
        if(m_bLoaded)
        {
            if(!m_bStopWork)
            {
                m_bStopWork = true;
                m_iFinishAcqFlag = true;
                if(m_pGralObj!=NULL && m_pGralObj->isRunning())
                {
                    qDebug()<<"m_pGrabObj is stop";
                    m_pGralObj->Stop();
                }

                m_camereInstant.StopAcquisition();
                m_camereInstant.IssueAcquisitionStopCommand();

                if(m_pConfigureObj!=NULL)
                {
                    if(m_pConfigureObj->UpdateConfigureFromDevice())
                        m_pConfigureObj->SlotConfigChanged();
                    if(m_pConfigureObj->UpdateSimplyConfigureFromDevice())
                        m_pConfigureObj->SlotSimplyConfigChanged();
                }
                ret = RETURN_OK;
            }
        }
    }
    catch(GenICam::GenericException &e)
    {
        qDebug()<< "error: AcquisitionStop"<<e.GetDescription();
    }
#endif
    return ret;
}
           
  1. 停止采集的语句就是下面这个比较重要的语句:

基本你执行了这个语句就已经可以起到停止采集的效果了。这里我们也看一下它的源码:

void GenTLConsumerImplHelper::CGenTLCamera<ProducerName>::StopAcquisition()
{
    Internal::LockGuard lock(m_Lock);

    CIH_CHECK_INITIALIZED();
    CIH_CHECK_ACQ_PREPARED();
    using namespace GenTL;
    GC_ERROR status = s_Producer.DSStopAcquisition(GetDataStreamHandle(), ACQ_STOP_FLAGS_DEFAULT);
    if (nullptr != m_ptrGrabResources.get())
        m_ptrGrabResources->SetCleanupLevel(GrabResources::FlushQueues);

    // Lock critical camera parameters that must not be changed while acquiring image data.
    GenApi::CIntegerPtr ptrLocked = GetParameter("TLParamsLocked");
    if (ptrLocked.IsValid() && GenApi::IsWritable(ptrLocked))
        ptrLocked->SetValue(0);

    if (status != GC_ERR_SUCCESS)
    {
        throw RUNTIME_EXCEPTION(s_Producer.CreateErrorMessage("Failed to stop the acquisition (DSStoptAcquisition failed)"));
    }
}
           

这里做的一个比较经典的操作是对相机的参数进行锁定,防止你在进行停止采集的过程对一些停止采集所需要的参数进行更改的过程,从而破坏这个停止采集的过程。

总结

关于这个相机的实时采图,难度应该不算很大,比较大的应该是触发采图,因为我几个函数设置的关系比较紧密,导致那个切换浪费了比较多的时间,不过那个后面会讲,这里就先把这个功能给完成了吧。如果有什么问题,欢迎提出。

继续阅读