Высокий динамический диапазон (HDR) и фотосъемка с низким светом

В этой статье показано, как использовать класс AdvancedPhotoCapture для записи фотографий с высоким динамическим диапазоном (HDR). Этот API также позволяет получить эталонный кадр из записи HDR до завершения обработки окончательного образа.

Другие статьи, связанные с записью HDR, включают:

Замечание

Начиная с Windows 10 версии 1709, запись видео и использование AdvancedPhotoCapture одновременно поддерживается. Это не поддерживается в предыдущих версиях. Это изменение означает, что вы можете одновременно подготовить LowLagMediaRecording и AdvancedPhotoCapture . Вы можете запускать или останавливать запись видео между вызовами MediaCapture.PrepareAdvancedPhotoCaptureAsync и AdvancedPhotoCapture.FinishAsync. Вы также можете вызвать AdvancedPhotoCapture.CaptureAsync во время записи видео. Однако некоторые сценарии AdvancedPhotoCapture, такие как съёмка HDR-фотографии во время записи видео, могут привести к изменению некоторых видеокадров из-за HDR, что ведет к негативному пользовательскому опыту. По этой причине список режимов, возвращаемых AdvancedPhotoControl.SupportedModes , будет отличаться во время записи видео. Это значение следует проверить сразу после запуска или остановки записи видео, чтобы убедиться, что нужный режим поддерживается в текущем состоянии записи видео.

Замечание

Начиная с Windows 10 версии 1709, когда в режиме HDR задан режим AdvancedPhotoCapture , параметр свойства FlashControl.Enabled игнорируется, и вспышка никогда не запускается. Для других режимов захвата, если FlashControl.Enabled включена, она переопределит параметры AdvancedPhotoCapture и приведет к тому, что будет сделана обычная фотография со вспышкой. Если для параметра Auto задано значение true, advancedPhotoCapture может или не использовать вспышку в зависимости от поведения драйвера камеры по умолчанию для условий в текущей сцене. В предыдущих выпусках настройка вспышки AdvancedPhotoCapture всегда переопределяет параметр FlashControl.Enabled.

Существует полный пример использования класса AdvancedPhotoCapture , который можно использовать для просмотра API, используемого в контексте или в качестве отправной точки для собственного приложения. Дополнительные сведения см. в примере расширенной записи камеры.

Запись фотографий HDR

Определение поддержки записи фотографий HDR на текущем устройстве

Метод записи HDR, описанный в этой статье, выполняется с помощью объекта AdvancedPhotoCapture . Не все устройства поддерживают запись HDR с помощью AdvancedPhotoCapture. Определите, поддерживает ли устройство, на котором сейчас работает ваше приложение, метод, получив объект MediaCapture, затем получив VideoDeviceController, a затем свойство AdvancedPhotoControl. Проверьте коллекцию SupportedModes контроллера видеоустройства, чтобы узнать, включает ли она AdvancedPhotoMode.Hdr. В этом случае съёмка HDR с помощью AdvancedPhotoCapture поддерживается.

m_hdrSupported = m_mediaCapture.VideoDeviceController.AdvancedPhotoControl.SupportedModes.Contains(Windows.Media.Devices.AdvancedPhotoMode.Hdr);

Настройка и подготовка объекта AdvancedPhotoCapture

Так как вам потребуется получить доступ к экземпляру AdvancedPhotoCapture из нескольких мест в коде, необходимо объявить переменную члена для хранения объекта.

private AdvancedPhotoCapture m_advancedCapture;

После инициализации объекта MediaCapture создайте объект AdvancedPhotoCaptureSettings и задайте для него режим AdvancedPhotoMode.Hdr. Вызовите метод Configure объекта AdvancedPhotoControl, передавая созданный объект AdvancedPhotoCaptureSettings.

Вызовите метод PrepareAdvancedPhotoCaptureAsync объекта MediaCapture, передавая объект ImageEncodingProperties, указывающий тип кодирования, который следует использовать. Класс ImageEncodingProperties предоставляет статические методы для создания кодировок изображений, поддерживаемых MediaCapture.

PrepareAdvancedPhotoCaptureAsync возвращает объект AdvancedPhotoCapture , который будет использоваться для запуска фотозахвата. Этот объект можно использовать для регистрации обработчиков для OptionalReferencePhotoCaptured и AllPhotosCaptured, которые рассматриваются далее в этой статье.

if (m_hdrSupported == false) return;

// Choose HDR mode
var settings = new AdvancedPhotoCaptureSettings { Mode = AdvancedPhotoMode.Hdr };

// Configure the mode
m_mediaCapture.VideoDeviceController.AdvancedPhotoControl.Configure(settings);

// Prepare for an advanced capture
m_advancedCapture =
    await m_mediaCapture.PrepareAdvancedPhotoCaptureAsync(ImageEncodingProperties.CreateUncompressed(MediaPixelFormat.Nv12));

// Register for events published by the AdvancedCapture
m_advancedCapture.AllPhotosCaptured += M_advancedCapture_AllPhotosCaptured;
m_advancedCapture.OptionalReferencePhotoCaptured += M_advancedCapture_OptionalReferencePhotoCaptured;

Сделать снимок HDR

Захват фотографии HDR путем вызова метода CaptureAsync объекта AdvancedPhotoCapture. Этот метод возвращает объект AdvancedCapturedPhoto , предоставляющий захваченную фотографию в свойстве Frame . Затем фотография сохраняется на диске.

try
{

    // Start capture, and pass the context object
    AdvancedCapturedPhoto advancedCapturedPhoto = await m_advancedCapture.CaptureAsync();

    using (var frame = advancedCapturedPhoto.Frame)
    {
        var fileName = String.Format("SimplePhoto_{0}_HDR.jpg", DateTime.Now.ToString("HHmmss"));
        StorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync(fileName);
        IRandomAccessStream stream = await photoFile.OpenAsync(FileAccessMode.ReadWrite);
        await RandomAccessStream.CopyAndCloseAsync(advancedCapturedPhoto.Frame, stream);
    }
}
catch (Exception ex)
{
    Debug.WriteLine("Exception when taking an HDR photo: {0}", ex.ToString());
}

Получение необязательного эталонного кадра

Процесс HDR захватывает несколько кадров, а затем композитирует их в один образ после того, как все кадры были записаны. Доступ к кадру можно получить после захвата, но до завершения всего процесса HDR с помощью обработки события OptionalReferencePhotoCaptured . Это не нужно делать, если вы заинтересованы только в окончательном результате фотографии HDR.

Это важно

OptionalReferencePhotoCaptured не вызывается на устройствах, поддерживающих оборудование HDR, и поэтому не создает эталонные кадры. Приложение должно обрабатывать ситуацию, когда это событие не вызывается.

Так как эталонный кадр выходит из контекста вызова CaptureAsync, механизм предоставляется для передачи сведений о контексте обработчику OptionalReferencePhotoCaptured . Сначала необходимо вызвать объект, содержащий сведения о контексте. Название и содержание этого объекта — по вашему усмотрению. В этом примере определяется объект с элементами для отслеживания имени файла и ориентации камеры записи.

public class MyAdvancedCaptureContextObject
{
    public string CaptureFileName;
    public PhotoOrientation CaptureOrientation;
}

Создайте новый экземпляр объекта контекста, заполните его члены, а затем передайте его в перегрузку метода CaptureAsync, которая принимает объект как параметр.

// Add the the capture time to the file name
var fileName = String.Format("SimplePhoto_{0}_HDR.jpg", DateTime.Now.ToString("HHmmss"));

// Create a context object, to identify the capture in the OptionalReferencePhotoCaptured event
var context = new MyAdvancedCaptureContextObject()
{
    CaptureFileName = fileName,
};

// Start capture, and pass the context object
AdvancedCapturedPhoto advancedCapturedPhoto = await m_advancedCapture.CaptureAsync(context);

В обработчике событий OptionalReferencePhotoCaptured приведите свойство Context объекта OptionalReferencePhotoCapturedEventArgs к классу объекта контекста. В этом примере изменяется имя файла, чтобы отличать опорный кадр от окончательного HDR-изображения, а затем сохраняется изображение на диск.

private async void M_advancedCapture_OptionalReferencePhotoCaptured(AdvancedPhotoCapture sender, OptionalReferencePhotoCapturedEventArgs args)
{
    // Retrieve the context (i.e. what capture does this belong to?)
    var context = args.Context as MyAdvancedCaptureContextObject;

    // Remove "_HDR" from the name of the capture to create the name of the reference
    var referenceName = context.CaptureFileName.Replace("_HDR", "");

    using (var frame = args.Frame)
    {
        await SaveCapturedFrameAsync(frame, referenceName, context.CaptureOrientation);
    }
}

Получите уведомление, когда все кадры будут захвачены

Запись фотографий HDR состоит из двух шагов. Сначала записывается несколько кадров, а затем кадры обрабатываются в окончательном образе HDR. Вы не можете инициировать другой захват, пока исходные кадры HDR по-прежнему фиксируются, но вы можете инициировать запись после того, как все кадры были записаны, но до завершения последующей обработки HDR. Событие AllPhotosCaptured возникает, когда записи HDR завершены, давая вам знать, что вы можете инициировать еще одну запись. Типичный сценарий — отключить кнопку захвата UI при начале захвата HDR, а затем включить её снова при возникновении AllPhotosCaptured.

private void M_advancedCapture_AllPhotosCaptured(AdvancedPhotoCapture sender, object args)
{
    // Update UI to enable capture button
}

Очистка объекта AdvancedPhotoCapture

После завершения записи приложения перед удалением объекта MediaCapture необходимо завершить работу объекта AdvancedPhotoCapture , вызвав FinishAsync и установив для переменной члена значение NULL.

await m_advancedCapture.FinishAsync();
m_advancedCapture = null;

Фотосъемка с низким освещением

Начиная с Windows 10 версии 1607 , AdvancedPhotoCapture можно использовать для записи фотографий с помощью встроенного алгоритма, который повышает качество фотографий, захваченных в параметрах низкого освещения. При использовании функции с низким светом класса AdvancedPhotoCapture система будет оценивать текущую сцену и, при необходимости, применять алгоритм для компенсации низкой освещенности. Если система определяет, что алгоритм не требуется, вместо этого выполняется обычный захват.

Прежде чем использовать съёмку при низкой освещённости, определите, поддерживает ли устройство, на котором выполняется ваше приложение, эту технику, получив объект MediaCapture, затем VideoDeviceController, и далее получив свойство AdvancedPhotoControl. Проверьте коллекцию SupportedModes контроллера видеоустройства, чтобы узнать, включает ли она AdvancedPhotoMode.LowLight. Если это так, поддерживается запись с низким светом с помощью AdvancedPhotoCapture .

m_lowLightSupported = m_mediaCapture.VideoDeviceController.AdvancedPhotoControl.SupportedModes.Contains(Windows.Media.Devices.AdvancedPhotoMode.LowLight);

После инициализации объекта MediaCapture создайте объект AdvancedPhotoCaptureSettings и задайте для него режим AdvancedPhotoMode.LowLight. Вызовите метод Configure объекта AdvancedPhotoControl, передавая созданный объект AdvancedPhotoCaptureSettings.

Вызовите метод PrepareAdvancedPhotoCaptureAsync объекта MediaCapture, передавая объект ImageEncodingProperties, указывающий тип кодирования, который следует использовать.

if (m_lowLightSupported == false) return;

// Choose LowLight mode
var settings = new AdvancedPhotoCaptureSettings { Mode = AdvancedPhotoMode.LowLight };
m_mediaCapture.VideoDeviceController.AdvancedPhotoControl.Configure(settings);

// Prepare for an advanced capture
m_advancedCapture =
    await m_mediaCapture.PrepareAdvancedPhotoCaptureAsync(ImageEncodingProperties.CreateJpeg());

Чтобы записать фотографию, вызовите CaptureAsync.

AdvancedCapturedPhoto advancedCapturedPhoto = await m_advancedCapture.CaptureAsync();
StorageFile photoFile = await KnownFolders.PicturesLibrary.CreateFileAsync($"Photo_{advancedCapturedPhoto.Mode}.jpg", CreationCollisionOption.GenerateUniqueName);
IRandomAccessStream stream = await photoFile.OpenAsync(FileAccessMode.ReadWrite);
await RandomAccessStream.CopyAndCloseAsync(advancedCapturedPhoto.Frame, stream);

Вы можете сделать несколько снимков при низкой освещенности без перенастройки объекта AdvancedPhotoCapture, но при окончании съёмки следует вызвать FinishAsync, чтобы очистить объект и связанные ресурсы.

await m_advancedCapture.FinishAsync();
m_advancedCapture = null;

Работа с объектами AdvancedCapturedPhoto

AdvancedPhotoCapture.CaptureAsync возвращает объект AdvancedCapturedPhoto , представляющий захваченную фотографию. Этот объект предоставляет свойство Frame , которое возвращает объект CapturedFrame , представляющий изображение. Событие OptionalReferencePhotoCaptured также предоставляет объект CapturedFrame в аргументе события. После получения объекта этого типа можно выполнить ряд действий, включая создание SoftwareBitmap или сохранение изображения в файле.

Получение SoftwareBitmap из захваченного кадра

Получите SoftwareBitmap из объекта CapturedFrame, просто получив доступ к свойству SoftwareBitmap этого объекта. Однако большинство форматов кодирования не поддерживают SoftwareBitmap с AdvancedPhotoCapture, поэтому необходимо проверить и убедиться, что свойство не равно NULL перед его использованием.

SoftwareBitmap bitmap;
if (advancedCapturedPhoto.Frame.SoftwareBitmap != null)
{
    bitmap = advancedCapturedPhoto.Frame.SoftwareBitmap;
}

В текущем выпуске единственный формат кодирования, поддерживающий SoftwareBitmap для AdvancedPhotoCapture , является несжатой NV12. Поэтому, если вы хотите использовать эту функцию, необходимо указать эту кодировку при вызове PrepareAdvancedPhotoCaptureAsync.

m_advancedCapture =
    await m_mediaCapture.PrepareAdvancedPhotoCaptureAsync(ImageEncodingProperties.CreateUncompressed(MediaPixelFormat.Nv12));

Конечно, вы всегда можете сохранить изображение в файл, а затем загрузить его в SoftwareBitmap на отдельном шаге. Дополнительные сведения о работе с SoftwareBitmap см. в статье "Создание, изменение и сохранение растровых изображений".