Variable photo sequence

This article shows you how to capture a variable photo sequence, which allows you to capture multiple frames of images in rapid succession and configure each frame to use different focus, flash, ISO, exposure, and exposure compensation settings. This feature enables scenarios like creating High Dynamic Range (HDR) images.

If you want to capture HDR images but don't want to implement your own processing algorithm, you can use the AdvancedPhotoCapture API to use the HDR capabilities built-in to Windows. For more information, see High Dynamic Range (HDR) photo capture.

Note

This article builds on concepts and code discussed in Basic photo, video, and audio capture with MediaCapture, which describes the steps for implementing basic photo and video capture. It is recommended that you familiarize yourself with the basic media capture pattern in that article before moving on to more advanced capture scenarios. The code in this article assumes that your app already has an instance of MediaCapture that has been properly initialized.

Set up your app to use variable photo sequence capture

In addition to the namespaces required for basic media capture, implementing a variable photo sequence capture requires the following namespaces.

using Windows.Media.Capture.Core;
using Windows.Media.Devices.Core;

Declare a member variable to store the VariablePhotoSequenceCapture object, which is used to initiate the photo sequence capture. Declare an array of SoftwareBitmap objects to store each captured image in the sequence. Also, declare an array to store the CapturedFrameControlValues object for each frame. This can be used by your image processing algorithm to determine what settings were used to capture each frame. Finally, declare an index that will be used to track which image in the sequence is currently being captured.

VariablePhotoSequenceCapture _photoSequenceCapture;
SoftwareBitmap[] _images;
CapturedFrameControlValues[] _frameControlValues;
int _photoIndex;

Prepare the variable photo sequence capture

After you have initialized your MediaCapture, make sure that variable photo sequences are supported on the current device by getting an instance of the VariablePhotoSequenceController from the media capture's VideoDeviceController and checking the Supported property.

var varPhotoSeqController = _mediaCapture.VideoDeviceController.VariablePhotoSequenceController;

if (!varPhotoSeqController.Supported)
{
    ShowMessageToUser("Variable Photo Sequence is not supported");
    return;
}

Get a FrameControlCapabilities object from the variable photo sequence controller. This object has a property for every setting that can be configured per frame of a photo sequence. These include:

This example will set a different exposure compensation value for each frame. To verify that exposure compensation is supported for photo sequences on the current device, check the Supported property of the FrameExposureCompensationCapabilities object accessed through the ExposureCompensation property.

var frameCapabilities = varPhotoSeqController.FrameCapabilities;

if (!frameCapabilities.ExposureCompensation.Supported)
{
    ShowMessageToUser("EVCompenstaion is not supported in FrameController");
    return;
}

Create a new FrameController object for each frame you want to capture. This example captures three frames. Set the values for the controls you want to vary for each frame. Then, clear the DesiredFrameControllers collection of the VariablePhotoSequenceController and add each frame controller to the collection.

var frame0 = new FrameController();
var frame1 = new FrameController();
var frame2 = new FrameController();

frame0.ExposureCompensationControl.Value = -1.0f;
frame1.ExposureCompensationControl.Value = 0.0f;
frame2.ExposureCompensationControl.Value = 1.0f;

varPhotoSeqController.DesiredFrameControllers.Clear();
varPhotoSeqController.DesiredFrameControllers.Add(frame0);
varPhotoSeqController.DesiredFrameControllers.Add(frame1);
varPhotoSeqController.DesiredFrameControllers.Add(frame2);

Create an ImageEncodingProperties object to set the encoding you want to use for the captured images. Call the static method MediaCapture.PrepareVariablePhotoSequenceCaptureAsync, passing in the encoding properties. This method returns a VariablePhotoSequenceCapture object. Finally, register event handlers for the PhotoCaptured and Stopped events.

try
{
    var imageEncodingProperties = ImageEncodingProperties.CreateJpeg();

    _photoSequenceCapture = await _mediaCapture.PrepareVariablePhotoSequenceCaptureAsync(imageEncodingProperties);

    _photoSequenceCapture.PhotoCaptured += OnPhotoCaptured;
    _photoSequenceCapture.Stopped += OnStopped;
}
catch (Exception ex)
{
    ShowMessageToUser("Exception in PrepareVariablePhotoSequence: " + ex.Message);
}

Start the variable photo sequence capture

To start the capture of the variable photo sequence, call VariablePhotoSequenceCapture.StartAsync. Be sure to initialize the arrays for storing the captured images and frame control values and set the current index to 0. Set your app's recording state variable and update your UI to disable starting another capture while this capture is in progress.

private async void StartPhotoCapture()
{
    _images = new SoftwareBitmap[3];
    _frameControlValues = new CapturedFrameControlValues[3];
    _photoIndex = 0;
    _isRecording = true;

    await _photoSequenceCapture.StartAsync();
}

Receive the captured frames

The PhotoCaptured event is raised for each captured frame. Save the frame control values and captured image for the frame and then increment the current frame index. This example shows how to get a SoftwareBitmap representation of each frame. For more information on using SoftwareBitmap, see Imaging.

void OnPhotoCaptured(VariablePhotoSequenceCapture s, VariablePhotoCapturedEventArgs args)
{

    _images[_photoIndex] = args.Frame.SoftwareBitmap;
    _frameControlValues[_photoIndex] = args.CapturedFrameControlValues;
    _photoIndex++;
}

Handle the completion of the variable photo sequence capture

The Stopped event is raised when all of the frames in the sequence have been captured. Update the recording state of your app and update your UI to allow the user to initiate new captures. At this point, you can pass the captured images and frame control values to your image processing code.

void OnStopped(object s, object e)
{
    _isRecording = false;
    MyPostProcessingFunction(_images, _frameControlValues, 3);
}

Update frame controllers

If you want to perform another variable photo sequence capture with different per frame settings, you don't need to completely reinitialize the VariablePhotoSequenceCapture. You can either clear the DesiredFrameControllers collection and add new frame controllers or you can modify the existing frame controller values. The following example checks the FrameFlashCapabilities object to verify that the current device supports flash and flash power for variable photo sequence frames. If so, each frame is updated to enable the flash at 100% power. The exposure compensation values that were previously set for each frame are still active.

var varPhotoSeqController = _mediaCapture.VideoDeviceController.VariablePhotoSequenceController;

if (varPhotoSeqController.FrameCapabilities.Flash.Supported &&
    varPhotoSeqController.FrameCapabilities.Flash.PowerSupported)
{
    for (int i = 0; i < varPhotoSeqController.DesiredFrameControllers.Count; i++)
    {
        varPhotoSeqController.DesiredFrameControllers[i].FlashControl.Mode = FrameFlashMode.Enable;
        varPhotoSeqController.DesiredFrameControllers[i].FlashControl.PowerPercent = 100;
    }
}

Clean up the variable photo sequence capture

When you are done capturing variable photo sequences or your app is suspending, clean up the variable photo sequence object by calling FinishAsync. Unregister the object's event handlers and set it to null.

await _photoSequenceCapture.FinishAsync();
_photoSequenceCapture.PhotoCaptured -= OnPhotoCaptured;
_photoSequenceCapture.Stopped -= OnStopped;
_photoSequenceCapture = null;