Поделиться через


Высокий динамический диапазон (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 .

Примечание.

В этой статье рассматриваются основные понятия и код, описанные в разделе "Базовый" фото, видео и аудиозапись с помощью MediaCapture, в котором описаны шаги по реализации базового фото и видеозахвата. Рекомендуется ознакомиться с основным шаблоном захвата мультимедиа в этой статье, прежде чем перейти к более сложным сценариям захвата. В коде этой статьи предполагается, что приложение уже имеет экземпляр MediaCapture, который был правильно инициализирован.

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

Расширенные пространства имен захвата фотографий

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

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

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

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

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

bool _hdrSupported;
private void IsHdrPhotoSupported()
{
    _hdrSupported = _mediaCapture.VideoDeviceController.AdvancedPhotoControl.SupportedModes.Contains(Windows.Media.Devices.AdvancedPhotoMode.Hdr);
}

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

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

private AdvancedPhotoCapture _advancedCapture;

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

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

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

if (_hdrSupported == false) return;

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

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

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

// Register for events published by the AdvancedCapture
_advancedCapture.AllPhotosCaptured += AdvancedCapture_AllPhotosCaptured;
_advancedCapture.OptionalReferencePhotoCaptured += AdvancedCapture_OptionalReferencePhotoCaptured;

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

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

try
{

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

    using (var frame = advancedCapturedPhoto.Frame)
    {
        // Read the current orientation of the camera and the capture time
        var photoOrientation = CameraRotationHelper.ConvertSimpleOrientationToPhotoOrientation(
            _rotationHelper.GetCameraCaptureOrientation());
        var fileName = String.Format("SimplePhoto_{0}_HDR.jpg", DateTime.Now.ToString("HHmmss"));
        await SaveCapturedFrameAsync(frame, fileName, photoOrientation);
    }
}
catch (Exception ex)
{
    Debug.WriteLine("Exception when taking an HDR photo: {0}", ex.ToString());
}

Большинство приложений фотографии хотят закодировать поворот захваченной фотографии в файл изображения, чтобы он отображался правильно другими приложениями и устройствами. В этом примере показано использование вспомогательного класса CameraRotationHelper для вычисления правильной ориентации файла. Этот класс описан и указан в полном объеме в статье "Обработка ориентации устройства с помощью MediaCapture".

Вспомогательный метод SaveCapturedFrameAsync , который сохраняет образ на диск, рассматривается далее в этой статье.

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

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

Внимание

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

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

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

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

// Read the current orientation of the camera and the capture time
var photoOrientation = CameraRotationHelper.ConvertSimpleOrientationToPhotoOrientation(
        _rotationHelper.GetCameraCaptureOrientation());
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,
    CaptureOrientation = photoOrientation
};

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

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

private async void 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 завершены, давая вам знать, что вы можете инициировать еще одну запись. Типичный сценарий — отключить кнопку захвата пользовательского интерфейса при начале записи HDR, а затем повторно создать ее при возникновении AllPhotosCaptured .

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

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

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

await _advancedCapture.FinishAsync();
_advancedCapture = null;

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

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

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

bool _lowLightSupported;
_lowLightSupported = 
_mediaCapture.VideoDeviceController.AdvancedPhotoControl.SupportedModes.Contains(Windows.Media.Devices.AdvancedPhotoMode.LowLight);

Затем объявите переменную-член для хранения объекта AdvancedPhotoCapture .

private AdvancedPhotoCapture _advancedCapture;

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

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

if (_lowLightSupported == false) return;

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

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

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

AdvancedCapturedPhoto advancedCapturedPhoto = await _advancedCapture.CaptureAsync();
var photoOrientation = ConvertOrientationToPhotoOrientation(GetCameraOrientation());
var fileName = String.Format("SimplePhoto_{0}_LowLight.jpg", DateTime.Now.ToString("HHmmss"));
await SaveCapturedFrameAsync(advancedCapturedPhoto.Frame, fileName, photoOrientation);

Как и в приведенном выше примере HDR, в этом примере используется вспомогательный класс с именем CameraRotationHelper , чтобы определить значение поворота, которое должно быть закодировано в изображение, чтобы оно отображалось правильно другими приложениями и устройствами. Этот класс описан и указан в полном объеме в статье "Обработка ориентации устройства с помощью MediaCapture".

Вспомогательный метод SaveCapturedFrameAsync , который сохраняет образ на диск, рассматривается далее в этой статье.

Вы можете записать несколько фотографий с низким светом без перенастройки объекта AdvancedPhotoCapture , но при завершении записи необходимо вызвать ГотовоAsync , чтобы очистить объект и связанные ресурсы.

await _advancedCapture.FinishAsync();
_advancedCapture = null;

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

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

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

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

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

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

_advancedCapture =
    await _mediaCapture.PrepareAdvancedPhotoCaptureAsync(ImageEncodingProperties.CreateUncompressed(MediaPixelFormat.Nv12));

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

Сохранение захваченного кадра в файл

Класс CapturedFrame реализует интерфейс IInputStream, поэтому его можно использовать в качестве входных данных для BitmapDecoder, а затем bitmapEncoder можно использовать для записи данных изображения на диск.

В следующем примере создается новая папка в библиотеке изображений пользователя, а файл создается в этой папке. Обратите внимание, что приложению потребуется включить возможность библиотеки изображений в файл манифеста приложения, чтобы получить доступ к этому каталогу. Затем файловый поток открывается для указанного файла. Затем вызывается bitmapDecoder.CreateAsync для создания декодера из CapturedFrame. Затем CreateForTranscodingAsync создает кодировщик из потока файлов и декодировщика.

Следующие шаги кодируют ориентацию фотографии в файл изображения с помощью BitmapProperties кодировщика. Дополнительные сведения об обработке ориентации при записи изображений см. в разделе "Обработка ориентации устройства с помощью MediaCapture".

Наконец, изображение записывается в файл с вызовом FlushAsync.

private static async Task<StorageFile> SaveCapturedFrameAsync(CapturedFrame frame, string fileName, PhotoOrientation photoOrientation)
{
    var folder = await KnownFolders.PicturesLibrary.CreateFolderAsync("MyApp", CreationCollisionOption.OpenIfExists);
    var file = await folder.CreateFileAsync(fileName, CreationCollisionOption.GenerateUniqueName);

    using (var inputStream = frame)
    {
        using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
        {
            var decoder = await BitmapDecoder.CreateAsync(inputStream);
            var encoder = await BitmapEncoder.CreateForTranscodingAsync(fileStream, decoder);
            var properties = new BitmapPropertySet {
                { "System.Photo.Orientation", new BitmapTypedValue(photoOrientation, PropertyType.UInt16) } };
            await encoder.BitmapProperties.SetPropertiesAsync(properties);
            await encoder.FlushAsync();
        }
    }
    return file;
}