Mario.Tapilouw

Friday, January 29, 2010

Using Euresys Multicam Library

There are several different frame grabbers and industrial cameras available in my Lab for experiment. One of them is from Euresys (sorry, this is not an advertisement) and there are several other brands. However most of the time I'm dealing with this card either with analog cameras (standard analog interface) or digital cameras (with cameralink interface).


I found that this library is very convenient to use. The disadvantage is that like other available SDK's, I found that the guide of the SDK is not quite human readable. So I was trying hard to understand how to use this, and happens to be after a lot of attempts (try, fail, ask support, fail, try again, etc.) I can understand some this library.

Therefore I want to write how to use this library in this blog. So if I have to use it again in the future, I can read it from this blog and so can you :). If there's some part that is not correct, please give a feedback to me. Thanks.

The process of using this library is divided into three parts:
1. Initialization, includes the initialization of the card(s), camera(s) and the callback function.
2. Image acquisition.
3. Stop image acquisition and memory cleanup.

Let's start from the first part, initialization. Depending on your coding platform, you can write your own code. I'm using C++ on a Borland C++ Builder compiler, if you're using Visual C++ then you might need to adjust this code to your coding platform. I use the sample code provided from Euresys.

  • Board and camera initialization
1. Initialization of driver

McOpenDriver(NULL);

2. Setting error message configuration

// Activate message box error handling
McSetParamInt(MC_CONFIGURATION, MC_ErrorHandling, MC_ErrorHandling_MSGBOX);
McSetParamStr(MC_CONFIGURATION, MC_ErrorLog, "error.log");

3. Setting board topology (check your board)

McSetParamInt(MC_BOARD + 1, MC_BoardTopology, MC_BoardTopology_MONO);

4. Creating a channel

McCreate(MC_CHANNEL, &m_Channel);
McSetParamInt(m_Channel, MC_DriverIndex, 1);

  • Creating a connection to a camera

1. Choose the channel for the camera (depends on the port).

McSetParamStr(m_Channel, MC_Connector, "M");

2. Select the cam file, check your camera

McSetParamStr(m_Channel, MC_CamFile, "VCC-G22V31CL_P120SC");

3. Set the exposure time

McSetParamInt(m_Channel, MC_Expose_us, 10000);

4. Set the color format

McSetParamInt(m_Channel, MC_ColorFormat, MC_ColorFormat_Y8);

5. Set the acquisition mode

McSetParamInt(m_Channel, MC_AcquisitionMode, MC_AcquisitionMode_HFR);

  • Setting the trigger mode

1. Choose the way the first acquisition is triggered

McSetParamInt(m_Channel, MC_TrigMode, MC_TrigMode_IMMEDIATE);

2. Choose the triggering mode for subsequent acquisitions

McSetParamInt(m_Channel, MC_NextTrigMode, MC_NextTrigMode_REPEAT);

3. Choose the number of images to acquire

McSetParamInt(m_Channel, MC_SeqLength_Fr, MC_INDETERMINATE);

  • Setting up the frame parameters

1. Retrieve image dimensions

McGetParamInt(m_Channel, MC_ImageSizeX, &m_SizeX);
McGetParamInt(m_Channel, MC_ImageSizeY, &m_SizeY);
McGetParamInt(m_Channel, MC_BufferPitch, &m_BufferPitch);

2. The memory allocation for the images is automatically done by Multicam when activating the channel. We only set the number of surfaces to be created by MultiCam.

McSetParamInt(m_Channel, MC_SurfaceCount, EURESYS_SURFACE_COUNT);

  • Enabling Multicam Signals

1. Enable MultiCam signals

McSetParamInt(m_Channel, MC_SignalEnable + MC_SIG_SURFACE_PROCESSING, MC_SignalEnable_ON);
McSetParamInt(m_Channel, MC_SignalEnable + MC_SIG_ACQUISITION_FAILURE, MC_SignalEnable_ON);

2. Register the callback function

McRegisterCallback(m_Channel, GlobalCallback, this);

  • Preparing bitmap info

1. Build bitmap info Y8

m_pBitmapInfo = (BITMAPINFO *) new BYTE[sizeof(BITMAPINFO) + 255*sizeof(RGBQUAD)];
m_pBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_pBitmapInfo->bmiHeader.biPlanes = 1;
m_pBitmapInfo->bmiHeader.biBitCount = 8;
m_pBitmapInfo->bmiHeader.biCompression = BI_RGB;
m_pBitmapInfo->bmiHeader.biSizeImage = 0;
m_pBitmapInfo->bmiHeader.biXPelsPerMeter = 0;
m_pBitmapInfo->bmiHeader.biYPelsPerMeter = 0;
m_pBitmapInfo->bmiHeader.biClrUsed = 0;
m_pBitmapInfo->bmiHeader.biClrImportant = 0;

for (int i = 0 ; i <>
{
m_pBitmapInfo->bmiColors[i].rgbBlue = (BYTE)i;
m_pBitmapInfo->bmiColors[i].rgbGreen = (BYTE)i;
m_pBitmapInfo->bmiColors[i].rgbRed = (BYTE)i;
m_pBitmapInfo->bmiColors[i].rgbReserved = 0;
}

m_pBitmapInfo->bmiHeader.biWidth = m_BufferPitch / (m_pBitmapInfo->bmiHeader.biBitCount/8) ;
m_pBitmapInfo->bmiHeader.biHeight = -(int)m_SizeY ;

  • Declaring the callback function

void WINAPI GlobalCallback(PMCSIGNALINFO SigInfo)
{
if (SigInfo && SigInfo->Context)
{
TForm1* pTForm1 = (TForm1*) SigInfo->Context ;
pTForm1->Callback(SigInfo);
}
}

  • Callback function

void TForm1::Callback(PMCSIGNALINFO SigInfo)
{
if (SigInfo->Signal == MC_SIG_SURFACE_PROCESSING)
{
// Update "current" surface address pointer
McGetParamInt(SigInfo->SignalInfo, MC_SurfaceAddr, (PINT32) &m_pCurrent);

//----------------------------------------
//
// Insert the eVision code here.
//
//----------------------------------------

// Post screen refresh message
RECT recpict;
recpict.left = 0;
recpict.top = 0;
recpict.right = m_SizeX-1;
recpict.bottom = m_SizeY-1;
InvalidateRect(Handle, &recpict, false);
}
else if (SigInfo->Signal == MC_SIG_ACQUISITION_FAILURE)
{
StatusBar1->SimpleText = m_StatusBarText.sprintf("Frame Rate: %.2f, Channel State: IDLE", 0);
MessageBox(NULL, "Acquisition Failure !", "PicoloVideoTrigger", MB_OK);
}
}

  • Start and Stop Grabbing

1. Start an acquisition sequence by activating the channel

McSetParamInt(m_Channel, MC_ChannelState, MC_ChannelState_ACTIVE);

2. Stop an acquisition sequence by deactivating the channel

McSetParamInt(m_Channel, MC_ChannelState, MC_ChannelState_IDLE);

  • Deactivating Channel and Cleaning Up

1. Set the channel to IDLE before deleting it.

McSetParamInt(m_Channel, MC_ChannelState, MC_ChannelState_IDLE);

2. Delete the channel

McDelete(m_Channel);

3. Terminate driver

McCloseDriver();

4. Delete bitmap info

if (m_pBitmapInfo) delete m_pBitmapInfo;


That's all, I will post the details in the next article because it's too long to be posted here.

Labels: , ,

10 Comments:

  • Hello,

    I'm grad student in Japan, trying to use a grablink

    I'm very glad to get near to a real sample program, after two days trying to figure out what was going on in the official codes (that I can not figure out how to use anyway).

    Anyway, I was wondering if you had a sample program that could just display the image coming from the cameras ? The simpler the better.

    Otherwise I have one big question : where do I create the image that can be processed ? Because here I see only the acquisition part.

    Thank you again for this post.

    Sebastien

    By Anonymous sebastien, at April 8, 2010 at 7:00 PM  

  • Hi Sebastien,

    Actually, I got this code from the official codes from Euresys. So, you might better start with the supplied code. I can explain every lines of the codes to you if you need help.

    The image that can be processed, good question, that is the first question that I had when I first learned Euresys. So, here's how it should be:

    - Create an EBW8Image object, something like:
    EImageBW8 frmEImageView;

    - Then set the size of the object:
    frmEImageView.SetSize(m_SizeX, m_SizeY);

    - Finally, you have to copy the buffer into the EImage object, put this code inside the Callback:

    frmEImageView[CcdID].SetImagePtr(m_SizeX, m_SizeY, (VOID *) m_pCurrent, m_BufferPitch * 8);

    Then you can write your algorithm to process the EImage instance either inside the callback if your code is not time consuming, or put in another thread instead.

    Hope this helps.

    By Blogger mario, at April 8, 2010 at 8:31 PM  

  • Hi,

    Thank you for the fast answer. I made some progress since yesterday. I can run some of the sample programs.

    I managed to configure my camera for running "grablinkSnapshot", which now works allright, but "grablinkHfr" doesn't work. When I try to use Hfr acquisition mode, I get a Hardware_configuration_error (-15). Does it mean my camera don't support this mode? (on paper, my camera should be able to go up to 400fps).

    Next question: what is the letter parameter for the MC_CONNECTOR setting? For me, it works with "M", but not with "A" (I had to change this in the sample codes).

    Last, but not least: I don't really understand how the callback works. I have no experience with either windows API or vcl (just some basics with Qt). Why do you use two functions (globalcallback and callback)? What is the SigInfo object? Is SigInfo the "this" pointer?

    My first code is working in snapshot acquisition, with a callback that does nothing. I'm trying to make an image display in the callback by combining sample programs code and yours (to make something that works without other API for now).

    I hope I can see an image from my own code by tonight.

    Sebastien

    By Anonymous sebastien, at April 9, 2010 at 1:21 PM  

  • Hi Sebastien,

    I suppose you're using Grablink Grablink Value card. "M" in MC_CONNECTOR means that you're using only one camera in your grabber.

    The Hardware_configuration_error means that your grabber doesn't support HFR mode, it's not the camera's problem. This mode is only supported in Grablink Express, Grablink Avenue, and Grablink Expert 2.

    Hope these help.

    By Blogger mario, at April 10, 2010 at 8:28 AM  

  • Hi,

    I am trying to write a program to simply grab an image. I am using the GrabLink Expert 2 and so far I have been following your post.

    I have the initialization done up to enabling the signal. However, I am stuck on calling the callback function. In the "McRegisterCallback(...,...,this);. I am getting an error saying " 'this' can only be referenced inside nonstatic member functions."

    And one last question, in your declaration of the GlobalCallback function, you use "TForm". Where does this class come from?

    Your feedback would be greatly appreciated.

    Mike

    By Anonymous Anonymous, at July 4, 2013 at 12:21 AM  

  • Hi Mike,
    The TForm is a built-in class in C++ Builder, so if we want to create a form, the form is an instance of TForm.
    I hope that answers your question.

    By Blogger mario, at July 11, 2013 at 6:16 AM  

  • Hi

    I'm looking for the fastest way to transfer a color frame grabbed with a Euresys card to an OpenCV Mat.

    Did you ever tried to manage this passage ?
    Would you share some advice ?

    By now I'm doing the following, but it looks twisted to me, there should be some better way.

    Thank you for any help !!
    Best Regards



    //Variables
    MCHANDLE hSurface;
    PUINT8 *pData = NULL;
    std::vector array_to_merge;

    cv::Mat matR;
    cv::Mat matG;
    cv::Mat matB;
    cv::Mat src;

    volatile PUINT8 m_ImageBufferR;
    volatile PUINT8 m_ImageBufferG;
    volatile PUINT8 m_ImageBufferB;



    //Surface retrieval
    hSurface = (MCHANDLE)CbInfo.SignalInfo;

    //RGB Planes
    McGetParamInt(hSurface, MC_SurfaceContext, (PINT32)&pData);
    McGetParamInt(hSurface, MC_SurfaceAddr + 0, (PINT32)&m_ImageBufferR);
    McGetParamInt(hSurface, MC_SurfaceAddr + 1, (PINT32)&m_ImageBufferG);
    McGetParamInt(hSurface, MC_SurfaceAddr + 2, (PINT32)&m_ImageBufferB);

    matR = cv::Mat(m_ImageSizeY, m_ImageSizeX, cv::IMREAD_GRAYSCALE, m_ImageBufferR);
    matG = cv::Mat(m_ImageSizeY, m_ImageSizeX, cv::IMREAD_GRAYSCALE, m_ImageBufferG);
    matB = cv::Mat(m_ImageSizeY, m_ImageSizeX, cv::IMREAD_GRAYSCALE, m_ImageBufferB);

    //Converting RGB Planes to a openCV/Mat object
    array_to_merge.push_back(matB);
    array_to_merge.push_back(matG);
    array_to_merge.push_back(matR);

    cv::merge(array_to_merge, src);

    By Blogger Lorenzo Viola, at April 14, 2016 at 6:54 AM  

  • Hi, Sorry for late response. Actually I have no experience in using cv::Mat for transferring Euresys data since I am not using Euresys cards anymore. When I wrote the code I was using IplImage. But your code seems to be right.

    By Blogger mario, at June 14, 2016 at 4:14 PM  

  • Hello, thank you anyway !

    PS : what are you using now ?

    Best Regards

    By Blogger Lorenz, at June 14, 2016 at 6:28 PM  

  • Hi Lorenzo, I am using Dalsa grabber card now... Dalsa is good but the technical support is not as good as Euresy, they are not responsive and sometimes they didn't answer the problems..

    By Blogger mario, at June 14, 2016 at 10:59 PM  

Post a Comment

<< Home