可变照片序列

本文向你演示如何捕获可变照片序列,允许你快速连续捕获图像的多个帧,并将每个帧配置为使用不同的焦点、闪光灯、ISO、曝光和曝光补偿设置。 此功能启用了创建高动态范围 (HDR) 图像等方案。

如果你想要捕获 HDR 图像,但不想要实现你自己的处理算法,你可以使用 AdvancedPhotoCapture API 来使用内置于 Windows 的 HDR 功能。 有关详细信息,请参阅高动态范围 (HDR) 照片捕获

注意

本文以使用 MediaCapture 捕获基本的照片、视频和音频中讨论的概念和代码为基础,该文章介绍了实现基本照片和视频捕获的步骤。 建议你先熟悉该文中的基本媒体捕获模式,然后再转到更高级的捕获方案。 本文中的代码假设你的应用已有一个正确完成初始化的 MediaCapture 的实例。

设置你的应用以使用可变照片序列捕获

除了基本媒体捕获所需的命名空间,实现可变照片序列捕获还需要以下命名空间。

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

声明成员变量以存储 VariablePhotoSequenceCapture 对象,该对象用于启动照片序列捕获。 声明 SoftwareBitmap 对象的数组以在序列中存储每个捕获的图像。 此外,声明数组以针对每个帧存储 CapturedFrameControlValues 对象。 可通过图像处理算法使用它,以确定哪些设置已用于捕获每个帧。 最后,声明一个将用于跟踪序列中当前正在捕获的图像的索引。

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

准备可变照片序列捕获

初始化你的 MediaCapture 之后,通过从媒体捕获的 VideoDeviceController 获取 VariablePhotoSequenceController 的实例并检查 Supported 属性,确保可变照片序列在当前设备上受支持。

var varPhotoSeqController = _mediaCapture.VideoDeviceController.VariablePhotoSequenceController;

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

从可变照片序列控制器获取 FrameControlCapabilities 对象。 此对象具有可针对照片序列的每帧配置的每个设置的属性。 其中包括:

此示例将为每个帧设置一个不同的曝光补偿值。 若要验证照片序列的曝光补偿在当前设备上是否受支持,请检查通过 ExposureCompensation 属性访问的 FrameExposureCompensationCapabilities 对象的 Supported 属性。

var frameCapabilities = varPhotoSeqController.FrameCapabilities;

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

为要捕获的每个帧创建一个新的 FrameController 对象。 此示例将捕获三个帧。 为你希望针对每个帧而变化的控件设置值。 接着,清除 VariablePhotoSequenceControllerDesiredFrameControllers 集合,然后将每个帧控制器添加到该集合。

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);

创建 ImageEncodingProperties 对象以设置要用于捕获的图像的编码。 调用静态方法 MediaCapture.PrepareVariablePhotoSequenceCaptureAsync,从而传入编码属性。 此方法返回 VariablePhotoSequenceCapture 对象。 最后,为 PhotoCapturedStopped 事件注册事件处理程序。

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);
}

启动可变照片序列捕获

若要启动可变照片序列捕获,请调用 VariablePhotoSequenceCapture.StartAsync。 请务必初始化用于存储捕获的图像和帧控件值的数组,并将当前索引设置为 0。 设置你的应用的录制状态变量并更新你的 UI,以禁止在执行此捕获时启动另一个捕获。

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

    await _photoSequenceCapture.StartAsync();
}

接收捕获的帧

针对每个捕获的帧引发 PhotoCaptured 事件。 保存帧控件值和该帧的已捕获图像,然后增加当前帧索引。 此示例显示了如何获取每个帧的 SoftwareBitmap 表示形式。 有关使用 SoftwareBitmap 的详细信息,请参阅图像处理

void OnPhotoCaptured(VariablePhotoSequenceCapture s, VariablePhotoCapturedEventArgs args)
{

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

处理可变照片序列捕获的完成

当已捕获序列中的所有帧时,将引发 Stopped 事件。 更新你的应用的录制状态并更新你的 UI,以允许用户启动新的捕获。 此时,你可以将捕获的图像和帧控件值传递给图像处理代码。

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

更新帧控制器

如果你想要使用不同的每帧设置执行另一个可变照片序列捕获,无需完全重新初始化 VariablePhotoSequenceCapture。 你可以清除 DesiredFrameControllers 集合并添加新的帧控制器,也可以修改现有的帧控制器值。 以下示例将检查 FrameFlashCapabilities 对象,以验证当前设备是否支持可变照片序列帧的闪光灯和闪光电源。 如果支持,将更新每个帧,以便在电源已满时启用闪光灯。 之前为每个帧设置的曝光补偿值仍然处于活动状态。

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;
    }
}

清理可变照片序列捕获

当你完成捕获可变照片序列或你的应用暂停时,通过调用 FinishAsync 清理可变照片序列对象。 取消注册对象的事件处理程序,并将其设置为 null。

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