About Grabber(2)

저번 리뷰(About Grabber(1)) 에 이어서 Grabber Manual을 위한 정리를 작성했습니다.


목차 (작성하며 변경예정)

1. Multi-spectral Grabber
2. QT
3. Ebus SDK
4. Code Review


2. QT


3. Ebus SDK

3.1 개요

[그림 1] Pleora Technologies

Ebus SDK 는 Multi-spectral 카메라 속 열화상 카메라(A35)를 개발한 회사인 Pleora technologies에서 배포한 카메라용 SDK(Software Development Kit) 입니다.

본 SDK는 이미지 송수신, 캡쳐, 디스플레이등 카메라를 이용하기 위한 모든 software tool이 간편화 되어서 개발자 및 연구자에게 애플리 케이션 개발을 단순화해준 좋은 라이브러리 입니다. 특히 모든 카메라가 GigE로 이뤄진 Multi-spectral Setup에 맞게 GigE를 지원하며, Window와 Linux에서 전부 활용가능하다는 장점이 있습니다.

저희는 본 SDK를 활용해서 열화상 카메라와 RGB 카메라 들과 연결하고 카메라가 촬영하고 있는 영상 데이터를 가져오는 작업을 수행했으며 저희가 SDK를 이용해서 진행한 작업은 다음과 같습니다.

  • 카메라와 QT 연결
  • 카메라로부터 영상 받아오기
  • 카메라와 QT 연결 해제

3.2 EBUS SDK 용어 정리

Ebus SDK를 이용한 각 동작 과정을 설명하기에 앞서 먼저 EBus SDK가 어떠한 과정으로 카메라와 연결하고 영상을 받아오는지 용어를 정리하면서 알아보겠습니다.

[그림2] 카메라 동작과정

위 그림이 SDK를 사용했을때 동작과정을 도식화 한 것입니다. 이 그림에서 중요하게 봐야할 용어는 PvDevice, PvPipeline, PvStream, PvBuffer가 있습니다. Application은 사용자이며 IP Engine은 카메라라고 생각하시면 될 것 같습니다.

각 용어를 토대로 전체적인 흐름을 설명하자면 다음과 같습니다.

  1. PvDevice를 통해서 사용자는 카메라의 정보를 받고 연결을 함
  2. PvDevice통해 연결된 카메라와 PvPipeline, PvStream 를 초기화함
  3. PvPipeline을 이용해서 PvStreamPvBuffer를 요청(?) 함
  4. PvStream을 이용해 카메라에서 받아온 Data Packet(PvBuffer)를 PvPipeline에 전달하며 최종적으로 사용자에게 전달됨

PvBuffer는 카메라에서 받아온 정보(Image)입니다.

* 본 SDK를 이용해 영상을 받는 방식으로는 위 과정처럼 PvPipeline과 PvStream을 전부사용한 방식과 PvStream 만을 이용한 방식이 있지만 본 메뉴얼에서는 전자만 다룰 것이며 후자가 궁금다면 공식 메뉴얼을 참고 바랍니다.

3.2 카메라 연결

카메라와 연결하기 위해서는 (1) 먼저 사용자의 플랫폼(컴퓨터, Jetson Xavier 등)과 연결된 기기의 Mac Address와 고유 IP를 알아야합니다. 그 후 (2) 그 정보를 이용해서 IP를 새로 할당을 해서 사용자의 플랫폼과 카메라가 연결 할 수 있도록 만들어야 합니다. (3) 모든 네트워크 관련 설정이 완료된다면 알고 있는 Mac Address와 새로 설정한 IP를 이용해 카메라와 사용자의 플랫폼을 연결해서 PvDevice변수를 초기화하게됩니다.

위 과정은 카메라에 대한 정보가 없을때 사용하는 방식으로, 만약 Mac Address를 알고 있다면 (1)과정을 생략해도 됩니다.

* 저희가 제작한 Multi-spectral Grabber에는 생략되어 있습니다. 따라서 Multi-spectral Grabber만 궁금하다면 3.2.2 Section으로 넘어가시면 됩니다.

3.2.1 (1) 주변 기기 검색

(1)과정을 하기 위해서는 Graphical User Interface(GUI)를 이용하는 방식과 코드를 이용하는 방식 두가지가 있으며 아래는 GUI를 이용한 방식의 예시입니다.

[그림3] Device Selection GUI 예시, 중간에 FLIR Ax5와 Flea3 FL3~~가 연결된 카메라를 나타냄

위 그림과 같이 GUI를 이용해서 현재 사용자의 플랫폼과 연결된 기기들을 확인과 선택을 할 수 있습니다. 또한 이 GUI를 이용해서 아직 IP가 할당되지 않은 기기를 그림 3속 좌하단에 있는 Set IP Address 버튼을 이용해 원하는 IP로 맵핑할 수 있습니다. 이렇게 간단한 GUI 를 SDK를 활용해 QT에서 키고 카메라 정보를 받아올려면 아래와 같은 코드를 실행시켜야 합니다.

C++ Example

#include <PvDeviceInfo.h>
#include <PvDeviceFinderWnd.h>
// 변수가 정의되면 무조건 ShowModal을 통해서 GUI를 켜야함
PvDeviceFinderWnd lFinderWnd;
// ShowModal()함수를 이용해 그림 3 과 같은 GUI를 띄우며, 만약 GUI를 띄우는데 문제가 생기면 return 됨
if (! lFinderWnd.ShowModal().IsOK() )
{
   return;
}
// 사용자가 선택한 디바이스의 정보를 GetSelected()를 이용해 받아오며 PvDeviceInfo 변수 에 저장함
PvDeviceInfo* lDeviceInfo = lFinderWnd.GetSelected();

다음은 코드를 이용해 Mac Address를 얻는 방법입니다.

이 방식은 살짝 무식한 방식이라고 할 수 있는데요, 요약하자면 “닥치는대로 다 연결 해보자” 방식입니다. 현재 연결된 모든 기기의 정보를 받아온 후 무작정 연결해봤을때 연결되는 기기를 사용하는 방식입니다. 이건 카메라를 한대만 연결했다는 가정이 깔려있다면 굉장히 유용하게 사용되겠지만 그게 아니라 저희 Multi-spectral Setup과 같이 카메라를 네대를 사용한다면 매우 부적절한 방식이라 할 수 있겠습니다. 이 방식을 사용하기 위한 코드는 다음과 같습니다.

*이 코드에서 나오는 PvResult 인자를 가지는 변수 IResult는 각 process가 정상동작했는지와 만약 비정상 동작을 했다면 어떤 문제가 있는지 받아오기 위한 변수이며 앞으로 모든 설명에서 사용될 것이니 기억해두면 좋습니다.

C++ Example

// 현재 연결된 기기 정보를 받아 오기 위한 변수 초기화
PvSystem lSystem;
// 각 process가 정상동작했는지와 만약 비정상 동작을 했다면 어떤 문제가 있는지 받아오기 위한 변수 초기화 
PvResult lResult;
// Deviceinfor 초기화
PvDeviceInfo *lDeviceInfo = NULL;
// 주변 기기찾을  때 최대 시간 200ms로 제한 .
lSystem.SetDetectionTimeout( 200 );
// Find()함수를 이용해 주변기기 탐색, 만약 연결된 기기가 없을 경우 find Error 출력 및 반환
lResult = lSystem.Find();
if( !lResult.IsOK() )
{  
    printf( "PvSystem::Find Error: %s", lResult.GetCodeString() );
    return -1;
}
// 현재 연결가능한 네트워크 갯수 저장
PvUInt32 lInterfaceCount = lSystem.GetInterfaceCount(); 
PvInterface * lInterface= NULL;
for( PvUInt32 x = 0; x < lInterfaceCount; x++ ) { 
         lInterface =         lSystem.GetInterface( x );
         // 현재 연결된 네트워크속 연결된 기기 갯수 반환 
         PvUInt32 lDeviceCount = lInterface->GetDeviceCount();
         // 현재 연결된 기기 갯수 만큼 for문 돌며 기기 정보 저장
         for( PvUInt32 y = 0; y < lDeviceCount; y++ ) {
                     lDeviceInfo = lInterface->GetDeviceInfo( y );
         }
}
PvDevice lDevice;
if( lDeviceInfo != NULL ) { 
       printf( "Connecting to %s\n", lDeviceInfo->GetMACAddress().GetAscii() );
      // 위에서 받아온 기기 정보를 이용해 기기와 연결 만약 기기연결에 실패할 경우 연결 실패한 기기의 MacAddress 출력 
      lResult = lDevice.Connect( lDeviceInfo );
       if ( !lResult.IsOK() )
       {
             printf( "Unable to connect to %s\n",lDeviceInfo->GetMACAddress().GetAscii() );
       }
}
else
{
    printf( "No device found\n" );
}

3.2.2 IP 할당

Mac Address를 원래 알고 있거나, 3.2.1 과정을 통해서 사용하고자하는 기기의 Mac Address를 알아낸 이후에 IP 할당 과정을 거치게 되며 코드는 아래와 같습니다.

*이 코드에서 나오는 PvResult 인자를 가지는 변수 IResult는 각 process가 정상동작했는지와 만약 비정상 동작을 했다면 어떤 문제가 있는지 받아오기 위한 변수이며 앞으로 모든 설명에서 사용될 것이니 기억해두면 좋습니다.

C++ Example

// Mac=기기의 Mac Address, IpAdr= 새로 기기에 부여할 Ip Address
void CamInitialize(const char* Mac, const char* IpAdr)
{
    // Device 초기화
    PvDeviceGEV* lDeviceSetIp = NULL;
    PvResult lResult;
    // SetIPConfiguration함수를 활용해서, 특정 Mac Address를 가지는 기기에 원하는 IP 할당
    lResult = lDeviceSetIp->SetIPConfiguration( Mac, IpAdr, "255.255.0.0");
    if (!lResult.IsOK()) cout << lResult.GetCodeString().GetAscii() << endl;
}

3.2.3 PvDevice 연결

[그림 4] PvDevice 역할

Ip까지 할당이 완료되게 된다면 PvDevice를 이용해서 카메라와 연결을 해야합니다. PvDevice는 카메라와 연결하는 역할하며 카메라와 직접적으로 소통하는 역할을 하며, 카메라를 동작시키고 동작을 멈추는등의 작업을 수행합니다.

  • 이러한 PvDevice를 이용해 카메라와 연결하기 위해서 아래와 같은 코드를 사용합니다. 아래 함수를 사용하면 이전 단계에서 얻은 Mac Address를 입력으로 사용해서 PvDevice를 반환 받게 됩니다.

C++ Example

// aConnectionID=MacAddress 
void ConnectToDevice( const char* aConnectionID)
{
    PvDevice *lDevice;
    PvResult lResult;

    // Mac Address를 활용해 Device와 연결
    cout << "Connecting to device." << endl;
    lDevice = PvDevice::CreateAndConnect( aConnectionID, &lResult );
    if ( lDevice == NULL )
    {
        cout << "Unable to connect to device: "
        << lResult.GetCodeString().GetAscii()
        << " ("
        << lResult.GetDescription().GetAscii()
        << ")" << endl;
    }
    return lDevice;
}

3.3 PvPipeline, PvStream 초기화

PvStream은 카메라의 PvBuffer(이미지) 를 직접적으로 받아오며, PvPipeline은 PvStream으로부터 PvBuffer를 받아서 사용자에게 넘겨주는 역할을 합니다. 이 두개를 초기화 하기 위해서는 먼저 특정 IP Address에 해당하는 PvStream을 연 후, PvStream과 PvDevice를 이용해 연결한 다음 PvPipeline을 연결해야합니다. 그 순서대로 Code와 함께 설명드리면 다음과 같습니다.

  • 먼저 3.2.2절에서 변경한 IP Address를 이용해서 PvStream을 open하는 code 입니다. CreateAndOpen 함수를 이 Pvstream을 PvBuffer를 받아오게 될 PvStream을 생성하며 만약 CreateAndOpen을 이용한 PvStream 생성에 문제가 생길 경우 어떤 문제가 있는지 출력하게 됩니다. 그리고 마지막으로 생성한 PvStream을 반환합니다.

C++ Example

// ConnectionID=Ip Address
PvStream* OpenStream( const PvString &aConnectionID )
{
    PvStream *lStream;
    PvResult lResult;

    // Open stream 
    cout << "Opening stream from device." << endl;
    lStream = PvStream::CreateAndOpen( aConnectionID, &lResult );
    if ( lStream == NULL )
    {
        cout << "Unable to stream from device. "
            << lResult.GetCodeString().GetAscii()
            << " ("
            << lResult.GetDescription().GetAscii()
            << ")"
            << endl;
    }

    return lStream;
}
  • 다음으로는 PvDevice와 PvStream을 연결하는 함수입니다. 두 변수를 연결해야지만 PvStream이 어떠한 카메라 즉 PvDevice로부터 PvBuffer를 가져올지 알 수 있습니다.

*이 코드 속 PvStreamGEV와 같이 GEV가 붙은 인자들은 GigE Vision을 뜻하며, GigE로 연결된 카메라를 다루기 위해서 사용됩니다.

C++ Example


PvStreamGEV* ConfigureStream( PvDevice *aDevice, PvStream *aStream  )
{
    PvStreamGEV* lStreamGEV = NULL;
    // If this is a GigE Vision device, configure GigE Vision specific streaming parameters
    PvDeviceGEV* lDeviceGEV = dynamic_cast<PvDeviceGEV *>( aDevice );
    if ( lDeviceGEV != NULL )
    {   
        // GigE 카메라에 맞게 변수를 변경합니다. 
        lStreamGEV = static_cast<PvStreamGEV *>( aStream );

        // PacketSize를 초기화합니다. 
        lDeviceGEV->NegotiatePacketSize();

        // PvDevice와 PvStream을 연결합니다. 
        lDeviceGEV->SetStreamDestination( lStreamGEV->GetLocalIPAddress(), lStreamGEV->GetLocalPort() );
    }

    return lStreamGEV;
}
  • 마지막으로 PvPipeline을 초기화하는 코드 입니다. 위에서 연결한 PvDevice와 PvStream을 인자로 하며, PvStream으로부터 가져올 Buffer의 갯수와 크기를 설정하고 오류가 있는 Buffer는 받지않는 설정까지 해서 마무리하게 됩니다.

C++ Example

PvPipeline *MakePipeline( PvDeviceGEV *aDevice, PvStream *aStream)
{

    PvResult lResult;

    // 카메라가 주는 Packet의 크기를 반환
    uint32_t lSize = aDevice->GetPayloadSize();

    PvPipeline *vPipeline;
    // PvStream과 PvPipeline을 연결
    vPipeline = new PvPipeline(aStream); 
    // Pipeline이 가져올 Buffer의 갯수와 크기를 설정합니다. 
    vPipeline->SetBufferCount( 16 );
    vPipeline->SetBufferSize(static_cast<uint32_t>(lSize));
    // 설정을 마치고 Pipeline을 초기화 완료 및 작동시작합니다.(작동하더라도 Device를 Acquisition 하지 않으면 영상을 받아오지않음). 
    lResult = vPipeline->Start();
    // PvPipeline이 설정값대로 작동하지 않으면 오류반환
    if (!lResult.IsOK())
    {
        cout << "Unable to start pipeline" << endl;
    }
    // 카메라로부터 영상을 받아올때 에러가 포함된 영상을 받아오는걸 막아주는 부분
    PvGenBoolean *lRequestMissingPackets = dynamic_cast<PvGenBoolean *>(aStream->GetParameters()->GetBoolean("RequestMissingPackets"));
    if ((lRequestMissingPackets != NULL) && lRequestMissingPackets->IsAvailable())
    {
        // Disabling request missing packets.
        lRequestMissingPackets->SetValue(false);
    }

    return vPipeline;
}
  • 위 모든 과정을 포함하는 함수입니다. 아래 코드와 같은 방식으로 PvDevice,PvStream 그리고 PvPipeline 까지 연결하며, 모든 연결이 완료 됐다면 이제 카메라로부터 영상을 받아올 준비가 완료됐다고 보시면 됩니다.

C++ Example

bool Connect(const char* Mac)
{
    // MacAddress를 활용해 카메라와 연결
    mDevice = ConnectToDevice(Mac);
    // GigE 카메라에 맞게 변수를 변경
    mDeviceGEV = dynamic_cast<PvDeviceGEV *>( mDevice );

    if ( mDevice != NULL )
    {
        // 설정한 Ip Address를 이용해 PvStream Open
        mStream = OpenStream( mDeviceGEV->GetIPAddress() );

        if (mStream != NULL)
        {
            // PvDevice와 PvStream 연결
            mStreamGEV = ConfigureStream(mDevice, mStream);
            // PvPipeline, PvStream, PvDevice 연결
            mPipeline = MakePipeline( mDeviceGEV, mStream );

            if ( mPipeline == NULL ) return false;
        }
        else
        {
            cout << "Disable to stream from camera" << endl;

            return false;
        }
    }
    else
    {
        cout << "Disable to connect to camera" << endl;
        return false;
    }

    return true;
}

3.3 카메라 설정하기

열화상 카메라와 칼라 카메라를 저희 용도에 맞게 사용하기 위해서는 다양한 환경설정을 바꿀 필요가 있습니다. 코드를 이용한 설정 변경방식을 보기전에 Ebus에서 제공하는 Grabber인 Ebus Player에서 카메라 설정하는 방식을 알아보고 Ebus Player와 비교하며 코드로 설정 변경하는 방식을 알아보겠습니다.

3.3.1 Ebus Player

[그림 5] Ebus Player

위가 Ebus Player 입니다. 현재 그림5의 상태는 열화상 카메라와 연결까지 다한 상태 입니다. 카메라와 연결을 한 후 카메라의 설정값을 변경해야하는데 그때 사용하는 버튼은 Parameters and Control 속 device Control버튼을 클릭하면 됩니다.

[그림 6] Ebus Player

Device Control을 클릭하면 그림 6과같은 GUI가 뜨게 되고 변경을 원하는 탭을 클릭 한 후 변경하면 됩니다. 만약 변경해야할 요소가 이곳에 없다면 그림 6 우상단에 있는 visibility를 Beginner에서 Guru로 변경하면 보일 수 도 있습니다. 변경했는데도 보이지 않을 경우 Parameters and Control 속 다른 버튼을 눌러서 찾아보시면 됩니다. 이렇게 Ebus Player를 이용한 설정값을 굉장히 간단하게 진행되며 코드로 이용한 설정 변경 또한 이 GUI만 잘 볼 수 있다면 쉽게 할 수 있습니다.

3.3.2 코드로 설정 변경

코드로 설정을 변경하는 것 또한 매우 간단하게 변경할 수 있습니다. 그 코드 예시는 아래와 같습니다. 보면 PvDeviceGEV 변수에서 카메라의 설정값을 받아온 후 단순하게 원하는 값으로 Set 하게 되면 설정값 변경이 완료 되는 것을 볼 수 있습니다.

C++ Example

// PvDeviceGEV* mDeviceGEV;
PvGenParameterArray *lDeviceParams = mDeviceGEV->GetParameters();
// Set{Type}Value(Feature Name,Value);
lDeviceParams->SetEnumValue("ExposureAuto",0);

하지만 이렇게 간단한 설정이 어려워지는 순간이 발생하는데, 그것은 저 SegEnumValue 로 변경할 수 있는 변경 값이 뭔지 알 수 없다는 점과 변경해야할 설정값을 이름(Feature Name)을 알 수 없다는 것 입니다.

이러한 어려운 점이 있지만 3.3.1 설명한 Ebus Player의 설정 GUI에서 원하는 설정을 찾을 수 있다면 코드 작성이 매우 쉬워집니다. 그림 6을 예시로 보면, 만약 받아오는 영상의 넓이 설정을 변경하고 싶다면 그림 과 같이 Width 를 찾은 후 클릭 하면 그림 하단과 같이 설정 정보가 보입니다. Width 설정의 이름은 Width 이며 type은 Integer이며 min과 max값이 있어서 그 사이 값으로 원하는 숫자를 넣으면 된다는 정보를 얻을 수 있습니다. 이 정보를 토대로 우리는 아래 코드와 같이 SetEnumValue 대신 SetIntegerValue를 사용하며 Feature Name와 Value를 아래와 같이 설정합니다.

lDeviceParams->SetIntegerValue("Width",256);

3.4 Acquisition

카메라에 Acquisition Start를 해줘야 영상을 촬영하기 시작합니다. 따라서 그림 4와 같이 Acquisition 을 담당하는 Device로부터 parameter를 받은 후 Start를 해줘야하며, Acquisition을 Start하기 전에 PvStream을 먼저 Enable해주며 코드는 아래와 같습니다.

C++ Example

// PvDeviceGEV로부터 Start와 Stop함수 받아오기 
PvGenCommand *lStart = dynamic_cast<PvGenCommand *>( lDeviceParams->Get( "AcquisitionStart" ) );
PvGenCommand *lStop = dynamic_cast<PvGenCommand *>( lDeviceParams->Get( "AcquisitionStop" ) );

// PvStream Enable && Acquisition Start
mDeviceGEV->StreamEnable();
lStart->Execute();

/* 
....r
*/
// Acquisition Stop
lStop->Execute();

3.5 영상 받아오기

영상을 받아 오는 순서는 그림 2와 같이 PvPipeline이 PvStream에 요청-> PvStream 이 카메라로부터 PvBuffer가져오기-> PvStream이 PvPipeline에게 PvBuffer 전달->PvPipeline으로부터 PvBuffer가져오기 순으로 진행이 됩니다. 하지만 이러한 과전은 한 문장이면 끝날정도로 매우 간단하게 진행됩니다.

RetrieveNextBuffer를 이용하면 PvBuffer를 원하는 변수에 저장하여 위에 모든 과정을 한번 진행합니다. Buffer를 가져온후 memory copy 방식인 memcpy를 이용해서 Buffer속에 있는 영상을 cv::Mat 혹은 QImage에 저장해서 원하는 곳에 사용하변 됩니다. 마지막으로 pipeline으로 가져온 buffer의 메모리를 해제하여 다음 buffer 받을 준비를 완료합니다.

C++ Example

// PvBuffer 초기화
PvBuffer *lBuffer = NULL;
// Buffer를 받아오기
lResult = mPipeline->RetrieveNextBuffer( &lBuffer, 0xFFFFFFFF, &lOperationResult);
// memcpy하기 위해서 Buffer의 크기를 반환 
uint32_t size = lBuffer->GetSize();
// Mutex를 이용해서 데이터 이동 안전성 확보 
Mmutex.lock();
cv::Mat Img(964, 1288, CV_8UC3);
memcpy(Img.data, lBuffer->GetDataPointer(), size);
Mmutex.unlock();
// Buffer point 해제
lResult = mPipeline->ReleaseBuffer(lBuffer);

위 코드는 간단한 설명을 위해서 에러 처리를 하지 않으며 단순히 한장의 영상을 받아오는 과정에 관한 것입니다. 하지만 우리는 지속적으로 영상을 받아야 하며, 영상을 받는 동안 에러처리까지 완료해야 Grabber가 멈춤없이 동작하게 됩니다. 따라서 모든 에러처리를 하며 지속적으로 영상을 받기 위한 코드가 필요하며 그 코드는 아래와 같습니다.

C++ Example

while(1)
{
    PvBuffer *lBuffer = NULL;
    PvResult lOperationResult;
    PvResult lResult;
    // PvStream과 PvPipeline 이 정상동작 할때 버퍼를 가져옴 
    if ((mStreamGEV != NULL) && mStreamGEV->IsOpen() &&
            (mPipeline != NULL) && mPipeline->IsStarted())
    {
          lResult = mPipeline->RetrieveNextBuffer( &lBuffer, 0xFFFFFFFF, &lOperationResult);
    }
    else{
        continue;
    }

    if ( lResult.IsOK())
        {
        if (  lOperationResult.IsOK() )
        {
            uint32_t size = lBuffer->GetSize();
            Mmutex.lock();
            cv::Mat Img(964, 1288, CV_8UC3);
            memcpy(Img.data, lBuffer->GetDataPointer(), size);
            Mmutex.unlock();
        }
        else
        {
            cout << lDoodle[ lDoodleIndex ] << " operational_T ERROR!! " << lOperationResult.GetCodeString().GetAscii()<<" "<< mdevice_index << "\r";
            if(lOperationResult.GetCode()==PvResult::Code::AUTO_ABORTED){
                mStreamGEV->QueueBuffer( lBuffer );
            }
        }
        lResult = mPipeline->ReleaseBuffer(lBuffer);
        if (!lResult.IsOK()){
            cout << lDoodle[ lDoodleIndex ] << " Release buffer ERROR! " << lResult.GetCodeString().GetAscii()<<" "<< mdevice_index<< "\r"
        }
    }
    else
    {
        // Retrieve buffer failure
        cout << lDoodle[ lDoodleIndex ] << " Retrieve buffer ERROR! " << lResult.GetCodeString().GetAscii()<<" "<< mdevice_index << "\r";
    }
    ++lDoodleIndex %= 6;
}  

3.6 카메라 연결 해제

Grabber를 종료할 때 강제로 프로그램을 종료해버리면 카메라와 사용자의 플랫폼과의 연결에 문제가 생기거나 카메라에 전기신호가 카메라가 잘못 전달되는 등의 문제가 생길 수 도 있습니다. 따라서 프로그램을 종료할때는 반드시 카메라와 컴퓨터가 연결한 순의 역순으로 하나씩 끊어가야합니다.

먼저 Acquisition Start를 통해서 영상을 받아 오고 있으니 Acquisition 부터 멈춥니다.

C++ Example

bool DisAcquisition(){
    PvGenParameterArray *lDeviceParams = mDeviceGEV->GetParameters();
    PvGenCommand *lStop = dynamic_cast<PvGenCommand *>( lDeviceParams->Get( "AcquisitionStop" ) );
    // Acquisition Stop
    lStop->Execute();

    // Disable streaming on the device
    mDeviceGEV->StreamDisable();

    // Abort all buffers from the stream and dequeue
    mStreamGEV->AbortQueuedBuffers();
}

그 다음 연결했었던 PvStream, PvPipeline, PvDevice를 제거해주므로써 안전하게 Grabber를 종료하게 됩니다.

C++ Example

bool zDisConnect()
{
    if ( mPipeline != NULL )
    {
        cout << "Delete Pipeline" << endl;
        delete mPipeline;

        if ( mStream != NULL )
        {
            cout << "Closing stream" << endl;
            mStream->Close();
            PvStream::Free( mStream );

            if ( mDevice != NULL )
            {
                cout << "Disconnecting device" << endl;
                mDevice->Disconnect();
                PvDevice::Free( mDevice);
            }
            else   return false;
        }
        else return false;
    }
    else return false;

    return true;
}

Author: 한 대찬

4 thoughts on “About Grabber(2)

    1. 2부작으로 끝낼려했으나… 3부작으로 마무리할려합니다.

  1. SDK 에서 제공하는 API를 잘 정리해주셨네요. 혹시 그래버제작에 필요한 멀티쓰레드 프로그래밍 부분도 다루실 생각이 있으신가요? 저는 그래버에서 가장 헷갈렸던 부분이 signal을 쓰레드끼리 주고받는 부분이었다고 생각합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다