PDA

View Full Version : Capture imagery from 6 cameras in parallel



wlucas
February 20, 2008, 20:33:43
Hi,

I'm new to using The Image Source IC Control library, and I had a few questions. I currently am using 6 DBK 21AF04 ImagingSource cameras. I have 3 firewire PCI cards with 2 cameras per card. I have run the IC Capture 2.0 software and it appears to be able to handle 6 cameras running in BY8, 640x480, 30fps without any bandwidth trouble. However, I am writing a program that needs to process these frames in parallel. The frames captured do no have to be synchronized. But, I do need to take advantage of the parallelism to increase the speed of the system.

The basic steps I need to do are this:

1. Grab an image
2. Run metrics on that image
3. If metrics > threshold
save the image

I just need this same thread to execute for each of the 6 cameras. Currently I have been using MFC worker threads to try to accomplish this, but after executing m_Grabber.startLive( false ); I get the following debug output

The thread 'DShowLib : CUCBTImpl Thread' (0xa50) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x994) has exited with code -2147024637 (0x80070103).
c:\csource\ic30\core\dshowlib\filtergraph.cpp(301) : Graph returned S_FALSE, so not yet started ...

Then after executing m_pSink->snapImages( 5 ); It crashes with this message

The thread 'DShowLib : CDispEventThread' (0xa0c) has exited with code 0 (0x0).
The thread 'Win32 Thread' (0x1d4) has exited with code 0 (0x0).

It seems that MFC worker threads is not the way to multithread this, is there another way to do multithreading to get this to work? Thanks for your time!

-Will Lucas

Stefan Geissler
February 21, 2008, 10:50:05
Will,

If I remember right, then the "initlibarary()" call must be done from the threads in order to avoid an error.

wlucas
February 21, 2008, 16:28:02
Hi Stefan,

Thanks for getting back to me! I tried adding the DShowLib::InitLibrary(), but it still seems to be crashing.

Here is my code that I'm currently using


CImgSrcCameraAdapter::CImgSrcCameraAdapter(int camNum)
{

m_CamNum = camNum;

ostringstream oss;

oss << "lastDeviceSetting" << m_CamNum << ".xml";

if( !setupDeviceFromFile( m_Grabber, oss.str() ) )
{

throw exception("Could not load grabber from file!");
}

m_Grabber.setOverlayBitmapPathPosition( ePP_NONE );

// create a black and white sink for the frame buffers.
m_pSink = FrameHandlerSink::create( eY800, 1 );

m_pSink->setSnapMode( true );
m_Grabber.setSinkType( m_pSink );

// Prepare the live mode, to get the output size if the sink.
if( !m_Grabber.prepareLive( false ) )
{

throw new exception("Could not render the VideoFormat into a eY800 sink.");
}

m_pSink->getOutputFrameType( m_Info );

// Allocate 5 image buffers of the above calculate buffer size.
for( int i = 0; i < 5; ++i )
{

m_pBuffers[i] = new BYTE[m_Info.buffersize];
}

// Create a new MemBuffer collection that uses our own image buffers.
m_pCollection = MemBufferCollection::create( m_Info, 5, m_pBuffers );
if( m_pCollection == 0 || !m_pSink->setMemBufferCollection( m_pCollection ) )
{

throw new exception("Could not set the new MemBufferCollection, because types do not match.");
}

}

void CImgSrcCameraAdapter::Run() {

// init the library for this thread
if( !DShowLib::InitLibrary( "XXXXXXX" ) )
{

fprintf( stderr, "The library could not be initialized ");
fprintf( stderr, "(invalid license key?).\n");
exit( 1 );
}

ostringstream oss;

oss << "cam" << m_CamNum << "*" << ".bmp";

// Start live mode for fast snapping. The live video will not be displayed,
// because false is passed to startLive().
m_Grabber.startLive( false );

// Snap 5 images. The images are copied to the MemBufferCollection the
// application created above.
m_pSink->snapImages( 5 );

// Stop the live video.
m_Grabber.stopLive();

// Close the device.
m_Grabber.closeDev();

// Save the five captured images in the MemBuffer collection to separate files.
m_pCollection->save( oss.str() );
}



I basically just converted the MemBufferCollection example into my threading interface, so that the Run() member function is called by the AfxBeginThread(...) function. If you need I can send you this project if that would help you troubleshoot more effectively. Thanks again for your time!

-Will Lucas

Stefan Geissler
February 22, 2008, 16:10:59
Will,

I was wrong, it was not InitLibrary, but "CoInitializeEx", that is used to initialize the Microsoft COM library.


But you could use a more simple approach. The Grabber objects start own threads. If you add a GrabberListener inherited class and add an instance to each grabber, then the GrabberListener::frameReady() is called within the grabber's thread. This would implement your multithreading automatically.

In order to do so, you must set the sinktype to false. Then each incoming frame is automatically copied into the ring buffer and frameReady() is called automatically (except a previous call of frameReady of the current grabber object has not returned yet)



for( int i = 0; i < 6; i++ )
{
// create a black and white sink for the frame buffers.
m_pSink[i] = FrameHandlerSink::create( eY800, 2 );

m_pSink[i]->setSnapMode( false);
m_Grabber[i].setSinkType( m_pSink );
m_Grabber[i].addListener(&m_cListener[i]);
}


I guess you can imagine the declarations of m_pSink, m_Grabber and m_cListener.
The size of the ringbuffer should be at least 2, otherwise the Grabbers will slow down. This means, while one frame is handled, the next one is copied into the ringbuffer and not thrown away.

I also answered by email to you, but with some less details.