Hello, everyone.

I'm working on rebuilding a GUI for an ultrasound machine and magnetic position sensor.
I'm rebuilding the GUI because the previous coder's work ran too slowly, not allowing for the 30 fps capture rate that the (less useful) company-provided code was capable of.


The original company code is driven off the comm port events from the magnetic sensor, with a timer that was used (at 10 fps) for captures where the sensor was unavailable.

I'm wondering if I can get much closer to 30 fps without the sensor inputs, but I'm not sure it's possible.

According to MSDN, the System.Timer interrupt interval has true precision down to 1/18th of a second, which is too slow for my purposes.

System.Environment.TickCount has "A minimum resolution of 500 milliseconds", which (I think) means it would only be capable of 2 fps.

But then, there is DateTime.Ticks, which appears to be able to give me a response in 100 nanosecond (.0001 Millisecond) intervals.

Is there a way to use DateTime.Ticks to create an interrupt that triggers more often than System.Timer?

Or should I just stick with the "30 fps with the sensor and 10 fps without" method?


Thanks in advance!

Thank you!

I'm not sure it's precisely what I'm looking for, but this will be very helpful in seeing where the previous version of the code is having trouble.

Hi,

If you need milisecond resolution just launch a new thread and call System.Threading.Thread.Sleep(<milisecond resolution you need>); from an infinite loop in that new thread with an abort flag to break out of loop.

Loren Soth

Okay, I'm trying that.

However, the way this capture card works makes it impossible to have two instances of the capturing object successfully operate off the camera simultaneously.

The capture class has a frame buffer of anywhere from 3 to 300+ frames, and the functions associated with it will handle the filling if I can just call the image storing function.

Also, this thread will have to be paused and resumed (presumably by thread.sleep(infinite) and thread.interrupt()) to take multiple captures.

The number of frames to capture, the rate, possibly even the location in memory may change between each acquisition run, so now I'd like some help with threading.


If I start this timer thread with a ref to the capture object (as part of the constructor), will the thread be able to make changes to/call on the object even if it moves in memory later on?

Or should/can I add a function that will update the threadTime class's variables before allowing it to start the aquisition process?

Hi,

You have multiple choices here, either you can instantiate the only allowed instance of the capture class (singleton pattern) in that new thread (for not blocking the GUI) or simply don't use multi thread but keep your infinite loop in your main thread (ex: while(keepGoing) {... xxx.capture(); System.Threading.Thread.Sleep(1000/FPS); Application.DoEvents(); ...} ) GUI wouldn't get affected as you let your thread wake up every 30ms and handle the windows message queue events. Also you might want to escalate the priority of your main thread.
Also why (and how) the memory location of the (one and only) capture class is changing ? Further more aren't all System.Object descendents are handled by reference.

Loren Soth

Hello, again!

I was under the impression that the reason why pointers were no longer used was because C# could shuffle memory storage space around to improve performance while the program was running, making the addresses in memory dynamic even for single instances of objects.

Guess I was wrong.

As for the other rhetoric, It sounds like you're far more knowledgable than I on C# (I'm rather new to C#, but I've experience with C/C++), so if you say that ref is going to behave, that is quite nice to know.

Now, if I understand you correctly, the option for keeping this in a single thread would be to do the following:

1. On clicking acquire, the program follows the handlers/callbacks to the frame capture function.

2. Within the continuous frame capture function, the code enters an infinite loop that executes
for the duration of the run, with some control statement, for example, while(NotDone).

3. Within this loop, I perform the capture, and then tell the system to sleep for the appropriate amount of time.

4. At the end of the sleep period, control is passed back to the thread, and it loops again.

5. However, during every cycle, the messages posted to the queue (such as a click on 'stop') will be processed using the .DoEvents() method?

Okay, I'll buy that. DoEvents() looks very handy, indeed. This will save me a lot of trouble!

Thank you very much!

Well, it works, after a fashion.
For a 5 second run at 30 fps, the loop completes in 4.969 seconds and missed 8 frames, (0.05333 frame loss ratio) which I attribute to converting 33.3333 ms per frame to an int and then dividing 33 by two.


My code for the loop:

private void CaptureLoop()
{
	int delay = Convert.ToInt32(frameDelay);
	if(delay ==33)
		delay=delay/2;
		refTick=System.Environment.TickCount;
		while(!stop&&((TotalFramesAcquired+missed)<local_config.MaxFramesAcquire))//this.nUDTotalFrames.Value))
		{
		curTick=System.Environment.TickCount;			
			
		if (!dt.AcquireFrame(TotalFramesAcquired++)) 
		{
			TotalFramesAcquired--;
		}		
		decimal oops = (curTick-refTick)/((TotalFramesAcquired + missed) *frameDelay);
		ticks.Add(curTick);
		if (oops > 1m) 
		{
			missed+= Convert.ToInt32(Math.Round(oops,0));	
		}
				
         System.Threading.Thread.Sleep(delay);
	Application.DoEvents();
	this.pbarCapture.Value=TotalFramesAcquired;
	}
  ContinuousStop();

}

I found that the system was taking twice as long to finish an acquisition run and also counting a number of 'misses' equal to the number of frames that were supposed to be captured. So, I hacked it and applied the /2. I get the feeling that I'll need to do something similar for the 15fps setting, only I'll be dividing by three.

Any ideas why the loop is so slow?

I tried setting the priority of this thread to highest, but that only ended up making the application take much longer to load.

Hi,

Delay of 33ms (30fps) for sleep wouldn't leave any time for the code in loop to execute, your sleep should be 33ms-code execution time. (If 33/2 is working perfectly for you then your code takes 17ms and for 15fps sleep should be 66-17 = 49ms; code time of 17ms is all dependent on machines CPU, process/thread priority and work load at the moment. If you boosted the priority from Windows Task Manager's Processes Tab you should have boosted process priority and not the thread priority; it helps but try bossting that very thread which your loop is in beacuse even the minimal .Net Application starts with 3 threads one being yours.

Loren Soth

Comments
Very insightful and helpful in resolving this important issue.

Ah, so the TOTAL time for one iteration of the loop should be 33 ms?
In retrospect, that should be a big ol' DUH on my part. Whoops.
...

I probably should've mentioned that this needs to be portable. Fortunately, it works rather well on the desktop system that is connected to the ultrasound machine, but I'll find out if the same holds true for a slower laptop system tomorrow.

Where would you suggest I boost the thread priority? I tried setting it to highest, but for some reason, it ended up making the initial load take a great deal longer than before. I didn't time it, but it felt like it took 20-30 more seconds. I didn't notice any real improvement in the performance, either.

Also: I found that there's an interesting progression.
30 fps requires ((delay/2)-1) to work well.
15 fps requires (delay*3)/4
10 fps requires (delay *13)/16 ...

I'll see if the 17 ms is really all that's necessary to make the delays work correctly.


If it turns out that this doesn't work, I'll need to try running this off the camera clock or something.

Thanks, that's really good to know!

Hi,

You can dynamically adjust the delay amount by checking the tick count difference at each iteration thus obtaining perfect timing independent of processor load, chosen FPS or hardware bottlenecks. Put " System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.AboveNormal; " to the first line of CaptureLoop();

Loren Soth

I see... I've set the thread priority in a slightly different location so that the entire application runs at highest. Any downsides to that?


And while I understand what you're saying, the implementation you're suggesting isn't clear. I'll work on it and post the result to see if I'm going about this in an appropriate manner.

Hi,

As long as the place you put the priority code is in the same thread as loop it won't matter. and for the delay here is a "still dynamic but not that much" kind of idea : let the loop rool for 4-5 times without delay and gather tickcount differences then average them to have see how much you code takes then Delay = (1000/FPS)-AverageCodeTimeInMSec;
Happy coding.

Loren Soth

Okay, this is what I've done...

private void DelayAdjust()
		{
			System.Int64 frequ = 0;
			if (QueryPerformanceFrequency(ref frequ) != 0)
			{
				
				dt.AllocateFrames(8);
				refTick=System.Environment.TickCount;
				
				System.Int64 count1 = 0;
				System.Int64 count2 = 0;			
				if (QueryPerformanceCounter(ref count1) != 0)
				{

					for(int i = 0; i<8; i++)
					{
						curTick=System.Environment.TickCount;			//int t       
						this.Times.Add(System.DateTime.Now);
				
						if (!dt.AcquireFrame(TotalFramesAcquired++)) 
						{
							TotalFramesAcquired--;
						}		
						decimal oops = (curTick-refTick)/((TotalFramesAcquired + missed) *frameDelay); //formerly decimal oops
						ticks.Add(curTick);
                
						if (oops > 1m) 
						{
							missed+= Convert.ToInt32(Math.Round(oops,0));					
						}
						Application.DoEvents();
						this.pbarCapture.Value=TotalFramesAcquired;
					}
					QueryPerformanceCounter(ref count2);
					//System.Int64 time_ms = (count2 - count1) * 1000 /(8* frequ);   
					offset= Convert.ToInt32((count2 - count1) * 1000 /(8* frequ));   
					this.lPerformance.Text="Delay(in ms): " +offset.ToString();//time_ms.ToString();
				}
				else
				{
					this.lPerformance.Text="Using preset delays";
				}
			}
			else
				this.lPerformance.Text="Using preset delays";

            dt.AllocateFrames(3);
			ticks.Clear();
			Times.Clear();
			TotalFramesAcquired = 0;
			missed = 0;
			this.pbarCapture.Value=TotalFramesAcquired;

		}

And it doesn't work very well.

When I run it, even if it reports that it is using the exact same offset as the funny equations I came up with, the capture runs take longer and miss more frames than when using presets.
This is rather annoying. I still haven't been able to get my boss to get ahold of his laptop so that I can see if the hard-coded version works acceptably on that, but I don't think this self-adjusting tick offset code will perform well.

This article has been dead for over six months. Start a new discussion instead.