天天看點

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)"));
    }
}
           

這裡做的一個比較經典的操作是對相機的參數進行鎖定,防止你在進行停止采集的過程對一些停止采集所需要的參數進行更改的過程,進而破壞這個停止采集的過程。

總結

關于這個相機的實時采圖,難度應該不算很大,比較大的應該是觸發采圖,因為我幾個函數設定的關系比較緊密,導緻那個切換浪費了比較多的時間,不過那個後面會講,這裡就先把這個功能給完成了吧。如果有什麼問題,歡迎提出。

繼續閱讀