High Dynamic Range (HDR) und Fotoaufnahme mit geringem Licht

In diesem Artikel erfahren Sie, wie Sie die AdvancedPhotoCapture-Klasse verwenden, um HDR-Fotos (High Dynamic Range) aufzunehmen. Mit dieser API können Sie auch einen Referenzframe aus der HDR-Aufnahme abrufen, bevor die Verarbeitung des endgültigen Bilds abgeschlossen ist.

Weitere Artikel im Zusammenhang mit HDR-Aufnahme sind:

Hinweis

Ab Windows 10, Version 1709, wird das Aufzeichnen von Videos und gleichzeitiger Verwendung von AdvancedPhotoCapture unterstützt. Dies wird in früheren Versionen nicht unterstützt. Diese Änderung bedeutet, dass Sie gleichzeitig über eine vorbereitete LowLagMediaRecording- und AdvancedPhotoCapture verfügen können. Sie können die Videoaufzeichnung zwischen Aufrufen von "MediaCapture.PrepareAdvancedPhotoCaptureAsync " und "AdvancedPhotoCapture.FinishAsync" starten oder beenden. Sie können advancedPhotoCapture.CaptureAsync auch während der Videoaufzeichnung aufrufen. Einige AdvancedPhotoCapture-Szenarien, z. B. das Aufnehmen eines HDR-Fotos beim Aufzeichnen von Videos, führen jedoch dazu, dass einige Videoframes durch die HDR-Aufnahme geändert werden, was zu einer negativen Benutzererfahrung führt. Aus diesem Grund unterscheidet sich die Liste der Modi, die von AdvancedPhotoControl.SupportedModes zurückgegeben werden, während das Video aufgezeichnet wird. Sie sollten diesen Wert unmittelbar nach dem Starten oder Beenden der Videoaufzeichnung überprüfen, um sicherzustellen, dass der gewünschte Modus im aktuellen Zustand der Videoaufzeichnung unterstützt wird.

Hinweis

Ab Windows 10, Version 1709, wird die Einstellung der FlashControl.Enabled-Eigenschaft ignoriert und der Blitz nie ausgelöst. Bei anderen Aufnahmemodi überschreibt flashControl.Enabled die AdvancedPhotoCapture-Einstellungen und bewirkt, dass ein normales Foto mit Blitz aufgenommen wird. Wenn "Auto " auf "true" festgelegt ist, verwendet AdvancedPhotoCapture möglicherweise den Blitz, abhängig vom Standardverhalten des Kameratreibers für die Bedingungen in der aktuellen Szene. In früheren Versionen überschreibt die AdvancedPhotoCapture-Blitzeinstellung immer die Einstellung "FlashControl.Enabled ".

Hinweis

Dieser Artikel baut auf Konzepten und Code auf, die unter Allgemeine Foto-, Video- und Audioaufnahme mit „MediaCapture“ erläutert werden. Dort werden die Schritte für die Implementierung einer grundlegenden Foto- und Videoaufnahme beschrieben. Wir empfehlen Ihnen, sich mit dem grundlegenden Medienaufnahmemuster in diesem Artikel vertraut zu machen, bevor Sie sich komplexeren Aufnahmeszenarien zuwenden. Der Code in diesem Artikel setzt voraus, dass Ihre App bereits über eine korrekt initialisierte MediaCapture-Instanz verfügt.

Es gibt ein universelles Windows-Beispiel, in dem die Verwendung der AdvancedPhotoCapture-Klasse veranschaulicht wird, die Sie verwenden können, um die im Kontext oder als Ausgangspunkt für Ihre eigene App verwendete API anzuzeigen. Weitere Informationen finden Sie im Beispiel "Camera Advanced Capture".

Erweiterte Fotoaufnahmenamespaces

Die Codebeispiele in diesem Artikel verwenden APIs in den folgenden Namespaces zusätzlich zu den Namespaces, die für die grundlegende Medienaufnahme erforderlich sind.

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

HDR-Fotoaufnahme

Ermitteln, ob die HDR-Fotoaufnahme auf dem aktuellen Gerät unterstützt wird

Die in diesem Artikel beschriebene HDR-Aufnahmetechnik wird mithilfe des AdvancedPhotoCapture-Objekts ausgeführt. Nicht alle Geräte unterstützen DIE HDR-Aufnahme mit AdvancedPhotoCapture. Ermitteln Sie, ob das Gerät, auf dem Ihre App derzeit ausgeführt wird, die Technik unterstützt, indem Sie den VideoDeviceController des MediaCapture-Objekts abrufen und dann die AdvancedPhotoControl-Eigenschaft abrufen. Überprüfen Sie die SupportedModes-Sammlung des Videogerätecontrollers, um festzustellen, ob es AdvancedPhotoMode.Hdr enthält. Wenn dies der Fall ist, wird DIE HDR-Aufnahme mit AdvancedPhotoCapture unterstützt.

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

Konfigurieren und Vorbereiten des AdvancedPhotoCapture-Objekts

Da Sie von mehreren Stellen im Code auf die AdvancedPhotoCapture-Instanz zugreifen müssen, sollten Sie eine Membervariable deklarieren, um das Objekt zu speichern.

private AdvancedPhotoCapture _advancedCapture;

Erstellen Sie in Ihrer App nach dem Initialisieren des MediaCapture-Objekts ein AdvancedPhotoCaptureSettings-Objekt, und legen Sie den Modus auf AdvancedPhotoMode.Hdr fest. Rufen Sie die Configure-Methode des AdvancedPhotoControl-Objekts auf, und übergeben Sie das von Ihnen erstellte AdvancedPhotoCaptureSettings-Objekt.

Rufen Sie die PrepareAdvancedPhotoCaptureAsync des MediaCapture-Objekts auf, und übergeben Sie ein ImageEncodingProperties-Objekt, das den Typ der Codierung angibt, die die Aufnahme verwenden soll. Die ImageEncodingProperties-Klasse stellt statische Methoden zum Erstellen der Bildcodierungen bereit, die von MediaCapture unterstützt werden.

PrepareAdvancedPhotoCaptureAsync gibt das AdvancedPhotoCapture -Objekt zurück, das Sie zum Initiieren der Fotoaufnahme verwenden. Mit diesem Objekt können Sie Handler für optionalReferencePhotoCaptured und AllPhotosCaptured registrieren, die weiter unten in diesem Artikel erläutert werden.

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;

Aufnehmen eines HDR-Fotos

Erfassen Sie ein HDR-Foto, indem Sie die CaptureAsync-Methode des AdvancedPhotoCapture-Objekts aufrufen. Diese Methode gibt ein AdvancedCapturedPhoto-Objekt zurück, das das aufgenommene Foto in seiner Frame-Eigenschaft bereitstellt.

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

Die meisten Foto-Apps möchten die Drehung eines aufgenommenen Fotos in die Bilddatei codieren, damit es von anderen Apps und Geräten korrekt angezeigt werden kann. Dieses Beispiel zeigt die Verwendung der Hilfsklasse CameraRotationHelper zum Berechnen der richtigen Ausrichtung für die Datei. Diese Klasse wird im Artikel "Behandeln der Geräteausrichtung mit MediaCapture" beschrieben und vollständig aufgeführt.

Die SaveCapturedFrameAsync-Hilfsmethode , die das Image auf einem Datenträger speichert, wird weiter unten in diesem Artikel erläutert.

Abrufen eines optionalen Referenzframes

Der HDR-Prozess erfasst mehrere Frames und zusammengesetzt sie dann in ein einzelnes Bild, nachdem alle Frames aufgenommen wurden. Sie können nach der Aufnahme auf einen Frame zugreifen, bevor der gesamte HDR-Prozess abgeschlossen ist, indem Sie das OptionalReferencePhotoCaptured-Ereignis behandeln. Sie müssen dies nicht tun, wenn Sie nur an dem endgültigen HDR-Fotoergebnis interessiert sind.

Wichtig

OptionalReferencePhotoCaptured wird nicht auf Geräten ausgelöst, die Hardware-HDR unterstützen und daher keine Referenzframes generieren. Ihre App sollte den Fall behandeln, in dem dieses Ereignis nicht ausgelöst wird.

Da der Referenzframe außerhalb des Kontexts des Aufrufs von CaptureAsync eingeht, wird ein Mechanismus bereitgestellt, um Kontextinformationen an den OptionalReferencePhotoCaptured-Handler zu übergeben. Zuerst sollten Sie ein Objekt aufrufen, das Ihre Kontextinformationen enthält. Der Name und der Inhalt dieses Objekts stehen Ihnen zur Seite. In diesem Beispiel wird ein Objekt definiert, das Elemente enthält, um den Dateinamen und die Kameraausrichtung der Aufnahme nachzuverfolgen.

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

Erstellen Sie eine neue Instanz ihres Kontextobjekts, füllen Sie dessen Member auf, und übergeben Sie es dann an die Überladung von CaptureAsync , die ein Objekt als Parameter akzeptiert.

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

Wandeln Sie im OptionalReferencePhotoCaptured-Ereignishandler die Context-Eigenschaft des OptionalReferencePhotoCapturedEventArgs-Objekts in die Kontextobjektklasse um. In diesem Beispiel wird der Dateiname geändert, um das Referenzframebild vom endgültigen HDR-Bild zu unterscheiden, und ruft dann die SaveCapturedFrameAsync-Hilfsmethode auf, um das Bild zu speichern.

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

Empfangen einer Benachrichtigung, wenn alle Frames erfasst wurden

Die HDR-Fotoaufnahme hat zwei Schritte. Zuerst werden mehrere Frames aufgenommen, und dann werden die Frames in das endgültige HDR-Bild verarbeitet. Sie können keine weitere Aufnahme initiieren, während die HDR-Quellframes noch aufgenommen werden, aber Sie können eine Aufnahme initiieren, nachdem alle Frames aufgenommen wurden, aber bevor die HDR-Nachbearbeitung abgeschlossen ist. Das AllPhotosCaptured-Ereignis wird ausgelöst, wenn die HDR-Aufnahmen abgeschlossen sind, sodass Sie wissen, dass Sie eine weitere Aufnahme initiieren können. Ein typisches Szenario besteht darin, die Aufnahmeschaltfläche Ihrer Benutzeroberfläche zu deaktivieren, wenn die HDR-Aufnahme beginnt und sie dann wiederverwenden kann, wenn AllPhotosCaptured ausgelöst wird.

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

Bereinigen des AdvancedPhotoCapture-Objekts

Wenn die App die Erfassung abgeschlossen hat, sollten Sie vor dem Entfernen des MediaCapture-Objekts das AdvancedPhotoCapture-Objekt herunterfahren, indem Sie FinishAsync aufrufen und die Membervariable auf NULL festlegen.

await _advancedCapture.FinishAsync();
_advancedCapture = null;

Fotoaufnahme mit geringem Licht

Ab Windows 10, Version 1607, kann AdvancedPhotoCapture zum Aufnehmen von Fotos mit einem integrierten Algorithmus verwendet werden, der die Qualität von Fotos verbessert, die in den Einstellungen mit geringem Licht aufgenommen werden. Wenn Sie die Low-Light-Funktion der AdvancedPhotoCapture-Klasse verwenden, wertet das System die aktuelle Szene aus und wendet bei Bedarf einen Algorithmus an, um niedrige Lichtverhältnisse zu kompensieren. Wenn das System feststellt, dass der Algorithmus nicht benötigt wird, wird stattdessen eine regelmäßige Erfassung durchgeführt.

Ermitteln Sie vor der Verwendung der Fotoaufnahme mit geringem Licht, ob das Gerät, auf dem Ihre App derzeit ausgeführt wird, die Technik unterstützt, indem Sie den VideoDeviceController des MediaCapture-Objekts abrufen und dann die AdvancedPhotoControl-Eigenschaft abrufen. Überprüfen Sie die SupportedModes-Sammlung des Videogerätecontrollers, um festzustellen, ob es AdvancedPhotoMode.LowLight enthält. Wenn dies der Fall ist, wird die Aufnahme mit geringem Licht mithilfe von AdvancedPhotoCapture unterstützt.

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

Deklarieren Sie als Nächstes eine Membervariable, um das AdvancedPhotoCapture-Objekt zu speichern.

private AdvancedPhotoCapture _advancedCapture;

Erstellen Sie in Ihrer App nach dem Initialisieren des MediaCapture-Objekts ein AdvancedPhotoCaptureSettings-Objekt, und legen Sie den Modus auf AdvancedPhotoMode.LowLight fest. Rufen Sie die Configure-Methode des AdvancedPhotoControl-Objekts auf, und übergeben Sie das von Ihnen erstellte AdvancedPhotoCaptureSettings-Objekt.

Rufen Sie die PrepareAdvancedPhotoCaptureAsync des MediaCapture-Objekts auf, und übergeben Sie ein ImageEncodingProperties-Objekt, das den Typ der Codierung angibt, die die Aufnahme verwenden soll.

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

Rufen Sie "CaptureAsync" auf, um ein Foto aufzunehmen.

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

Wie im obigen HDR-Beispiel wird in diesem Beispiel eine Hilfsklasse namens "CameraRotationHelper " verwendet, um den Drehungswert zu bestimmen, der in das Bild codiert werden soll, damit er von anderen Apps und Geräten ordnungsgemäß angezeigt werden kann. Diese Klasse wird im Artikel "Behandeln der Geräteausrichtung mit MediaCapture" beschrieben und vollständig aufgeführt.

Die SaveCapturedFrameAsync-Hilfsmethode , die das Image auf einem Datenträger speichert, wird weiter unten in diesem Artikel erläutert.

Sie können mehrere Fotos mit geringem Licht erfassen, ohne das AdvancedPhotoCapture-Objekt neu zu konfigurieren. Wenn Sie die Aufzeichnung abgeschlossen haben, sollten Sie FinishAsync aufrufen, um das Objekt und die zugehörigen Ressourcen zu bereinigen.

await _advancedCapture.FinishAsync();
_advancedCapture = null;

Arbeiten mit AdvancedCapturedPhoto-Objekten

AdvancedPhotoCapture.CaptureAsync gibt ein AdvancedCapturedPhoto -Objekt zurück, das das aufgenommene Foto darstellt. Dieses Objekt macht die Frame -Eigenschaft verfügbar, die ein CapturedFrame -Objekt zurückgibt, das das Bild darstellt. Das OptionalReferencePhotoCaptured-Ereignis stellt auch ein CapturedFrame-Objekt in seinen Ereignisargumenten bereit. Nachdem Sie ein Objekt dieses Typs abgerufen haben, gibt es eine Reihe von Aktionen, die Sie damit erledigen können, z. B. das Erstellen einer SoftwareBitmap oder das Speichern des Bilds in einer Datei.

Abrufen einer SoftwareBitmap aus einem CapturedFrame

Es ist trivial, ein SoftwareBitmap-Objekt aus einem CapturedFrame-Objekt abzurufen, indem einfach auf die SoftwareBitmap-Eigenschaft des Objekts zugegriffen wird. Die meisten Codierungsformate unterstützen SoftwareBitmap mit AdvancedPhotoCapture jedoch nicht, daher sollten Sie überprüfen und sicherstellen, dass die Eigenschaft vor der Verwendung nicht NULL ist.

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

In der aktuellen Version ist das einzige Codierungsformat, das SoftwareBitmap für AdvancedPhotoCapture unterstützt, nicht komprimiert NV12. Wenn Sie dieses Feature verwenden möchten, müssen Sie diese Codierung angeben, wenn Sie PrepareAdvancedPhotoCaptureAsync aufrufen.

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

Natürlich können Sie das Bild immer in einer Datei speichern und dann in einem separaten Schritt in eine SoftwareBitmap laden. Weitere Informationen zum Arbeiten mit SoftwareBitmap finden Sie unter Erstellen, Bearbeiten und Speichern von Bitmapbildern.

Speichern eines CapturedFrame in einer Datei

Die CapturedFrame-Klasse implementiert die IInputStream-Schnittstelle, sodass sie als Eingabe für einen BitmapDecoder verwendet werden kann, und anschließend kann ein BitmapEncoder verwendet werden, um die Bilddaten auf den Datenträger zu schreiben.

Im folgenden Beispiel wird ein neuer Ordner in der Bildbibliothek des Benutzers erstellt und eine Datei in diesem Ordner erstellt. Beachten Sie, dass Ihre App die Funktion "Bildbibliothek" in die App-Manifestdatei aufnehmen muss, um auf dieses Verzeichnis zuzugreifen. Anschließend wird ein Dateidatenstrom in der angegebenen Datei geöffnet. Als Nächstes wird bitmapDecoder.CreateAsync aufgerufen, um den Decoder aus dem CapturedFrame zu erstellen. Anschließend erstellt CreateForTranscodingAsync einen Encoder aus dem Dateidatenstrom und dem Decoder.

Die nächsten Schritte codieren die Ausrichtung des Fotos in die Bilddatei mithilfe der BitmapProperties des Encoders. Weitere Informationen zum Behandeln der Ausrichtung beim Erfassen von Bildern finden Sie unter "Behandeln der Geräteausrichtung mit MediaCapture".

Schließlich wird das Bild mit einem Aufruf von FlushAsync in die Datei geschrieben.

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