I think the answer is easier shown with a project: AcqSave.zip (11.9 KB). The Stemmer.Cvb and Stemmer.Cvb.Wpf assemblies need to be installed also (see the downloads). The app expects the camera to be in software trigger mode. If it is not it will save a lot of images .
In the project I implemented the ISoftwareTrigger via a Button
. The MainWindow
also shows a live image. The MainViewModel
provides the live Image
and commands for triggering and app-closing. The whole program logic is in the TriggerSaver
model class.
The MainWindow
creates the MainViewModel
which in turn creates the TriggerSaver
which opens the first GenICam device it finds. Then the app is run async via WPF’s Dispatcher
. Thus the main logic runs on the UI thread. That is not a bad thing as all the code is written async. The Image.Save
is executed via .Net’s task pool (Task.Run
).
The Device
is encapsulated in the model class TriggerSaver
. Only the live Image
is publicly available plus the use cases of running the app and sending (double) software triggers. Depending on the camera used you probably want to modify the TriggerSaver.SendSoftwareTrigger
method.
The app logic is written in a way that we wait for two images and save them as soon as they arrive. We utilize 's IRingBuffer interface to keep the acquisition code simple without loosing images. We use RingBufferLockMode.On
and manually unlock acquired Image
s via the using
-block in SaveAndUnlockImageAsync
. Depending on your acquisition rate and hard disk/ssd used you might need to increase the buffer count on the ring buffer to handle processing peaks (see TriggerSaver.SetupDevice
).
Here is the core logic:
public async Task RunAsync(CancellationToken cancellationToken)
{
var stream = _device.Stream;
stream.RingBuffer.LockMode = RingBufferLockMode.On;
stream.Start();
try
{
long counter = 0;
while (!cancellationToken.IsCancellationRequested)
{
try
{
var savesFinished = new Task[2];
for (int i = 0; i < 2; i++)
{
var imageTask = stream.WaitForAsync(AcquisitionTimeout);
savesFinished[i] = SaveAndUnlockImageAsync(imageTask, counter++);
}
await Task.WhenAll(savesFinished);
}
catch (TimeoutException)
{
// swallow timeouts, but we need to check for cancellation
}
}
}
finally
{
stream.Abort();
}
}
private async Task SaveAndUnlockImageAsync(Task<StreamImage> imageTask, long imageNumber)
{
Debug.Assert(imageTask != null);
using (var image = await imageTask)
{
await SaveImageAsync(image, imageNumber);
}
}
private static Task SaveImageAsync(Image image, long imageNumber)
{
Debug.Assert(image != null);
return Task.Run(() =>
image.Save(Path.Combine(StorageDirectory, $"{imageNumber}.bmp"))
);
}