Machine Vision

cancel
Showing results for 
Search instead for 
Did you mean: 

How to timestamp Camera Link frames from IMAQ using C

I'm capturing several different cameras using a Camera Link interface (PCIe-1429 and PCIe-1430 cards), and the NI-IMAQ C API. I'm having difficulty saving captures to disk and later syncing them up. For various reasons I cannot use triggering. To synchronize them it seems my best option is to timestamp the individual frames, but that opens the question of how to do this and if it's sufficient to synchronize them frame for frame later.

What I'm thinking now is to register a callback for the FRAME_START signal using the function imgSessionWaitSignalAsync (or rather imgSessionWaitSignalAsync2). FRAME_START should be the first signal I can get; not exactly when the frame was captured, due to integration time and electronics delay, but as good as I can get.

Problem is registering that callback doesn't tell me which frame the time should be associated with. As I'm using a ring buffer I will certainly get the FRAME_START signal perhaps several frame times in advance of when I actually use imgSessionExamineBuffer and write the frame to disk. Perhaps in the callback I can call imgGetAttribute on the IMG_ATTR_FRAME_COUNT or IMG_ATTR_LAST_VALID_FRAME attribute.

My two questions are:
1) When exactly is FRAME_START signaled?
2) In the callback should I use IMG_ATTR_FRAME_COUNT or IMG_ATTR_LAST_VALID_FRAME and (as I suspect) do I need to increment the result to get the frame currently being acquired?

Thank you for any insight,
-Josh Doe
0 Kudos
Message 1 of 4
(5,293 Views)

I have found an answer to my first question in of all places the NI-IMAQ User Manual (http://www.ni.com/pdf/manuals/370160d.pdf). I've been so used to trusting the Function Reference for everything I forgot the user manual had useful information.

 

According to the manual FRAME_START "... becomes TRUE when the device detects the first valid pixel in the current region of interest." If I have a ROI that starts other than (0,0) there would be some delay but it seems FRAME_START is the best I can get.

 

Any ideas on this? I would imagine this is a very common scenario of timestamping frames, but perhaps I'm mistaken.

0 Kudos
Message 2 of 4
(5,284 Views)

Hi Joshuadoe,

 

Have you taken a look at some of the suggestions here or here? I realize that you are using C and Cameralink but some of the concepts can be brought over (a lot of the functions in labVIEW have a similar name in C). 

 

Timestamping isn't rare but usually it's based off of a trigger or the time is already part of the metadata coming from the camera.

Message 3 of 4
(5,265 Views)

Thanks O54E,

I saw that first post but didn't read too carefully as I wasn't using LabVIEW. I read it again and what it suggests is basically what I tried doing yesterday, but using a loop versus a callback (which I presume can't be done in LabVIEW). I install an asynchronous callback on the FRAME_START signal and in the callback query ATTR_IMG_FRAME_COUNT. The callback should be called when frame N (zero based) is being acquired, but since it is not acquired yet the frame count should also be N (one based). I tried this with two cameras and then tried synchronizing them later. I was able to do this but there was approximately a 7 frame difference between what the timestamps and the image itself indicated. I'm not so much interested in the exact time any particular frame was acquired, but ensuring that two images at t=0s were taken at the same moment as well as at t=100s. If this frame offset is consistent then I have already achieved my objective.

 

Any further comments on this? I've pasted a code snippet below to pass along the way I'm doing this.

 

 

void IMAQClass::thread() { int ret; uInt32 *bufAddr=0, reqBufNum = 0, curBufNum=0; // register callback and start acquisition ret = imgSessionWaitSignalAsync2(m_sId, IMG_SIGNAL_STATUS, IMG_FRAME_START, IMG_SIGNAL_STATE_RISING, ImaqCallback, this); ret = imgSessionStartAcquisition(m_sId); while(!m_stopRequested) { // write timestamp and image to disk ret=imgSessionExamineBuffer2(m_sId, reqBufNum, &curBufNum, (void**)&bufAddr); m_file->write(m_timestamps[curBufNum],1,23); m_file->write(bufAddr,1,m_info.imageSize); ret = imgSessionReleaseBuffer(m_sId); reqBufNum = curBufNum+1; } imgSessionStopAcquisition(m_sId); } uInt32 ImaqCallback(SESSION_ID sid, IMG_ERR err, IMG_SIGNAL_TYPE signalType, uInt32 signalIdentifier, void* userdata) { static uInt32 val; if(signalIdentifier==IMG_FRAME_START) { imgGetAttribute(m_sId, IMG_ATTR_FRAME_COUNT, &val); m_timestamps[val] = to_iso_string(t); } return 1; // rearm callback }

 

 

 

0 Kudos
Message 4 of 4
(5,246 Views)