Getting Started with CVB.Net

Processing with Stemmer.Cvb.Async (Windows Forms)

In the last post we have seen how to do image processing with the StreamHandler:

And there I also stated, that if you want to have a fluent UI, you should use the Stemmer.Cvb.Async functionality.

Disclaimer: this quickly becomes an advanced topic if you do parallel processing!

You have been warned… :wink: We integrate nicely with the asynchronous and parallel processing capabilities of .Net.

Stream is an IObservable

One way we have seen above with Rx:

:cvb: Streams implement the IObservable<T> interface which enables you to .Subscribe to the Stream. The Stream pushes the acquired Images from a long-running Task to your registered IObserver<T> (where T is StreamImage). The StreamHandler utilizes this, in fact. It adds to that implementation the synchronzation to the UI thread-context to make your life easier. But if you want to do processing outside the UI thread-context, the IObservable<T>.Subscribe is a good place to start.

Stemmer.Cvb.Async Acquisition

In this post I describe a way that is more like the StreamHandler (it originates from the UI thread-context), but is more flexible. To make this work you first need to add these two namespace:

using Stemmer.Cvb.Async;
using Stemmer.Cvb.Driver;

The first one is for the async extension methods an the second one for the StreamImage which is returned by the .Wait method. For this to work you only need to rewrite your grabCheckBox_CheckedChanged event handler like this:

private async void grabCheckBox_CheckedChanged(object sender, EventArgs e)
{
  if (grabCheckBox.Checked)
  {
    Device.Stream.Start();
    openButton.Enabled = false;
    try
    {
      while (grabCheckBox.Checked)
      {
        using (StreamImage image = await Device.Stream.WaitAsync())
        {
          // processing
        }
      }
    }
    catch (OperationCanceledException)
    {
      // acquisition was aborted
    }
    finally
    {
      openButton.Enabled = true;
    }
  }
  else
  {
    if (Device.Stream.IsRunning)
      Device.Stream.Abort();
  }
}

Note the async keywork added in the method signature and the

await Device.Stream.WaitAsync()

call. Windows Forms is async/await capable so that if you check the grabCheckBox this method enters the then-branch of the if-conditional. It all runs in the UI thread-context. All these calls are reasonable fast, so that you won’t experience any lags. Until you reach the .Wait. This is now an awaitable .WaitAsync. The await cooperatively waits for the operation being finished and continues on after wards. While waiting, though, it gives control back to the UI so it stays reactive.

If you uncheck the grabCheckBox this method is reentered(!), but now goes to the else branch which .Aborts the acquisition. When there is a free slot in Windows Forms message queue handling, the still running grabCheckBox_CheckedChanged then-branch is ‘unthawed’ and also exits (as .Checked is now false). Also, dependent on when the .Abort() was issued, the .Wait may have been canceled (resulting in the OperationCanceledException).

Ok, why use this instead of the events? Because you are more flexible. You could

  • use WaitFor if you expect images to arrive in a certain time-frame
  • wait on two or more cameras simultanously
  • have a driver that does not support .Abort()
  • :wink:

Exit Handling

Exit handling should always be a priority if you do async or even parallel processing (e.g. via Task.Run). As long as you only perform short tasks in the UI thread, your are fine if you call

grabCheckBox.Checked = false;

in the void Dispose(bool disposing) method if disposing is true.

If you do your own parallel processing, then you must wait until that is finished in exit scenarios. I recommend creating a TaskCompletionSource<bool> (it has to have some type) like this (this code is inserted where the comment // Processing... is placed:

var tsc = new TaskCompletionSource<bool>();
_processingFinished = tsc.Task; // _processingFinished is of type Task
await Task.Run(() =>
{
  // lengthy processing

  // when finished:
  tsc.TrySetResult(true);
});

And then register the FormClosing event:

if (grabCheckBox.Checked)
{
  grabCheckBox.Checked = false;
  await _processingFinished;
}

This is safe as the TaskCompletionSource is created in the UI thread-context and the FormClosing event is also executed in the UI thread-context.

1 Like