Rentang dinamis tinggi (HDR) dan pengambilan foto cahaya rendah

Artikel ini menunjukkan kepada Anda cara menggunakan kelas AdvancedPhotoCapture untuk mengambil foto rentang dinamis tinggi (HDR). API ini juga memungkinkan Anda untuk mendapatkan bingkai referensi dari tangkapan HDR sebelum pemrosesan gambar akhir selesai.

Artikel lain yang terkait dengan pengambilan HDR meliputi:

Catatan

Dimulai dengan Windows 10, versi 1709, merekam video dan menggunakan AdvancedPhotoCapture secara bersamaan didukung. Ini tidak didukung di versi sebelumnya. Perubahan ini berarti Anda dapat menyiapkan LowLagMediaRecording dan AdvancedPhotoCapture secara bersamaan. Anda dapat memulai atau menghentikan perekaman video antara panggilan ke MediaCapture.PrepareAdvancedPhotoCaptureAsync dan AdvancedPhotoCapture.FinishAsync. Anda juga dapat memanggil AdvancedPhotoCapture.CaptureAsync saat video sedang direkam. Namun, beberapa skenario AdvancedPhotoCapture , seperti menangkap foto HDR saat merekam video akan menyebabkan beberapa bingkai video diubah oleh pengambilan HDR, menghasilkan pengalaman pengguna yang negatif. Untuk alasan ini, daftar mode yang dikembalikan oleh AdvancedPhotoControl.SupportedModes akan berbeda saat video direkam. Anda harus memeriksa nilai ini segera setelah memulai atau menghentikan perekaman video untuk memastikan bahwa mode yang diinginkan didukung dalam status perekaman video saat ini.

Catatan

Dimulai dengan Windows 10, versi 1709, ketika AdvancedPhotoCapture diatur ke mode HDR, pengaturan properti FlashControl.Enabled diabaikan dan lampu kilat tidak pernah diaktifkan. Untuk mode pengambilan lainnya, jika FlashControl.Enabled, itu akan menimpa pengaturan AdvancedPhotoCapture dan menyebabkan foto normal ditangkap dengan flash. Jika Otomatis diatur ke true, AdvancedPhotoCapture mungkin atau mungkin tidak menggunakan lampu kilat, tergantung pada perilaku default driver kamera untuk kondisi di adegan saat ini. Pada rilis sebelumnya, pengaturan lampu kilat AdvancedPhotoCapture selalu menggantikan pengaturan FlashControl.Enabled .

Catatan

Artikel ini dibangun berdasarkan konsep dan kode yang dibahas dalam pengambilan foto, video, dan audio Dasar dengan MediaCapture, yang menjelaskan langkah-langkah untuk menerapkan pengambilan foto dan video dasar. Kami menyarankan agar Anda membiasakan diri dengan pola pengambilan media dasar dalam artikel tersebut sebelum beralih ke skenario pengambilan yang lebih canggih. Kode dalam artikel ini mengasumsikan bahwa aplikasi Anda sudah memiliki instans MediaCapture yang telah diinisialisasi dengan benar.

Ada Sampel Windows Universal yang menunjukkan penggunaan kelas AdvancedPhotoCapture yang dapat Anda gunakan untuk melihat API yang digunakan dalam konteks atau sebagai titik awal untuk aplikasi Anda sendiri. Untuk informasi selengkapnya lihat, sampel Pengambilan Tingkat Lanjut Kamera.

Namespace pengambilan foto tingkat lanjut

Contoh kode dalam artikel ini menggunakan API di namespace berikut selain namespace yang diperlukan untuk pengambilan media dasar.

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

Pengambilan foto HDR

Tentukan apakah pengambilan foto HDR didukung pada perangkat saat ini

Teknik penangkapan HDR yang dijelaskan dalam artikel ini dilakukan menggunakan objek AdvancedPhotoCapture . Tidak semua perangkat mendukung penangkapan HDR dengan AdvancedPhotoCapture. Tentukan apakah perangkat tempat aplikasi Anda saat ini berjalan mendukung teknik dengan mendapatkan VideoDeviceController objek MediaCapture dan kemudian mendapatkan properti AdvancedPhotoControl. Periksa koleksi SupportedModes pengontrol perangkat video untuk melihat apakah itu menyertakan AdvancedPhotoMode.Hdr. Jika ya, pengambilan HDR menggunakan AdvancedPhotoCapture didukung.

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

Mengonfigurasi dan menyiapkan objek AdvancedPhotoCapture

Karena Anda harus mengakses instans AdvancedPhotoCapture dari beberapa tempat dalam kode Anda, Anda harus mendeklarasikan variabel anggota untuk menyimpan objek.

private AdvancedPhotoCapture _advancedCapture;

Di aplikasi Anda, setelah Anda menginisialisasi objek MediaCapture, buat objek AdvancedPhotoCaptureSettings dan atur mode ke AdvancedPhotoMode.Hdr. Panggil metode Konfigurasi objek AdvancedPhotoControl, melewati objek AdvancedPhotoCaptureSettings yang Anda buat.

Panggil objek MediaCapturePrepareAdvancedPhotoCaptureAsync, meneruskan objek ImageEncodingProperties yang menentukan jenis pengodean yang harus digunakan pengambilan. Kelas ImageEncodingProperties menyediakan metode statis untuk membuat pengodean gambar yang didukung oleh MediaCapture.

PrepareAdvancedPhotoCaptureAsync mengembalikan objek AdvancedPhotoCapture yang akan Anda gunakan untuk memulai pengambilan foto. Anda dapat menggunakan objek ini untuk mendaftarkan handler untuk OptionalReferencePhotoCaptured dan AllPhotosCaptured yang dibahas nanti di artikel ini.

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;

Mengambil foto HDR

Ambil foto HDR dengan memanggil metode CaptureAsync objek AdvancedPhotoCapture. Metode ini mengembalikan objek AdvancedCapturedPhoto yang menyediakan foto yang diambil di properti Bingkainya .

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

Sebagian besar aplikasi fotografi akan ingin mengodekan rotasi foto yang diambil ke dalam file gambar sehingga dapat ditampilkan dengan benar oleh aplikasi dan perangkat lain. Contoh ini menunjukkan penggunaan kelas pembantu CameraRotationHelper untuk menghitung orientasi yang tepat untuk file. Kelas ini dijelaskan dan tercantum secara lengkap dalam artikel Menangani orientasi perangkat dengan MediaCapture.

Metode pembantu SaveCapturedFrameAsync , yang menyimpan gambar ke disk, dibahas nanti di artikel ini.

Mendapatkan bingkai referensi opsional

Proses HDR menangkap beberapa bingkai dan kemudian menggabungkannya menjadi satu gambar setelah semua bingkai diambil. Anda bisa mendapatkan akses ke bingkai setelah diambil tetapi sebelum seluruh proses HDR selesai dengan menangani peristiwa OptionalReferencePhotoCaptured . Anda tidak perlu melakukan ini jika Anda hanya tertarik pada hasil foto HDR akhir.

Penting

OptionalReferencePhotoCaptured tidak dinaikkan pada perangkat yang mendukung HDR perangkat keras dan karenanya tidak menghasilkan bingkai referensi. Aplikasi Anda harus menangani kasus di mana peristiwa ini tidak dinaikkan.

Karena bingkai referensi keluar dari konteks panggilan ke CaptureAsync, mekanisme disediakan untuk meneruskan informasi konteks ke handler OptionalReferencePhotoCaptured . Pertama, Anda harus memanggil objek yang akan berisi informasi konteks Anda. Nama dan isi obyek ini terserah Anda. Contoh ini menentukan objek yang memiliki anggota untuk melacak nama file dan orientasi kamera tangkapan.

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

Buat instans baru objek konteks Anda, isi anggotanya, lalu teruskan ke kelebihan CaptureAsync yang menerima objek sebagai parameter.

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

Di penanganan aktivitas OptionalReferencePhotoCaptured, transmisikan properti Context objek OptionalReferencePhotoCapturedEventArgs ke kelas objek konteks Anda. Contoh ini memodifikasi nama file untuk membedakan gambar bingkai referensi dari gambar HDR akhir lalu memanggil metode pembantu SaveCapturedFrameAsync untuk menyimpan gambar.

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

Menerima pemberitahuan ketika semua bingkai telah diambil

Pengambilan foto HDR memiliki dua langkah. Pertama, beberapa bingkai ditangkap, dan kemudian bingkai diproses ke gambar HDR akhir. Anda tidak dapat memulai pengambilan lain saat bingkai HDR sumber masih diambil, tetapi Anda dapat memulai pengambilan setelah semua bingkai diambil tetapi sebelum pasca-pemrosesan HDR selesai. Peristiwa AllPhotosCaptured dinaikkan ketika tangkapan HDR selesai, memberi tahu Anda bahwa Anda dapat memulai pengambilan lain. Skenario umumnya adalah menonaktifkan tombol tangkapan UI Anda saat pengambilan HDR dimulai dan kemudian mengaktifkannya kembali ketika AllPhotosCaptured dinaikkan.

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

Membersihkan objek AdvancedPhotoCapture

Ketika aplikasi Anda selesai mengambil, sebelum membuang objek MediaCapture , Anda harus mematikan objek AdvancedPhotoCapture dengan memanggil FinishAsync dan mengatur variabel anggota Anda ke null.

await _advancedCapture.FinishAsync();
_advancedCapture = null;

Pengambilan foto bersinar rendah

Dimulai dengan Windows 10, versi 1607, AdvancedPhotoCapture dapat digunakan untuk mengambil foto menggunakan algoritma bawaan yang meningkatkan kualitas foto yang diambil dalam pengaturan cahaya rendah. Ketika Anda menggunakan fitur cahaya rendah dari kelas AdvancedPhotoCapture , sistem akan mengevaluasi adegan saat ini dan, jika diperlukan, menerapkan algoritma untuk mengimbangi kondisi cahaya rendah. Jika sistem menentukan bahwa algoritma tidak diperlukan, pengambilan reguler dilakukan sebagai gantinya.

Sebelum menggunakan pengambilan foto ringan, tentukan apakah perangkat tempat aplikasi Anda saat ini berjalan mendukung teknik dengan mendapatkan VideoDeviceController objek MediaCapture dan kemudian mendapatkan properti AdvancedPhotoControl. Periksa koleksi SupportedModes pengontrol perangkat video untuk melihat apakah itu menyertakan AdvancedPhotoMode.LowLight. Jika ya, pengambilan cahaya rendah menggunakan AdvancedPhotoCapture didukung.

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

Selanjutnya, nyatakan variabel anggota untuk menyimpan objek AdvancedPhotoCapture .

private AdvancedPhotoCapture _advancedCapture;

Di aplikasi Anda, setelah Anda menginisialisasi objek MediaCapture , buat objek AdvancedPhotoCaptureSettings dan atur mode ke AdvancedPhotoMode.LowLight. Panggil metode Konfigurasi objek AdvancedPhotoControl, melewati objek AdvancedPhotoCaptureSettings yang Anda buat.

Panggil objek MediaCapturePrepareAdvancedPhotoCaptureAsync, meneruskan objek ImageEncodingProperties yang menentukan jenis pengodean yang harus digunakan pengambilan.

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

Untuk mengambil foto, panggil 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);

Seperti contoh HDR di atas, contoh ini menggunakan kelas pembantu yang disebut CameraRotationHelper untuk menentukan nilai rotasi yang harus dikodekan ke dalam gambar sehingga dapat ditampilkan dengan benar oleh aplikasi dan perangkat lain. Kelas ini dijelaskan dan tercantum secara lengkap dalam artikel Menangani orientasi perangkat dengan MediaCapture.

Metode pembantu SaveCapturedFrameAsync , yang menyimpan gambar ke disk, dibahas nanti di artikel ini.

Anda dapat mengambil beberapa foto bersinar rendah tanpa mengonfigurasi ulang objek AdvancedPhotoCapture , tetapi ketika Anda selesai menangkap, Anda harus memanggil FinishAsync untuk membersihkan objek dan sumber daya terkait.

await _advancedCapture.FinishAsync();
_advancedCapture = null;

Bekerja dengan objek AdvancedCapturedPhoto

AdvancedPhotoCapture.CaptureAsync mengembalikan objek AdvancedCapturedPhoto yang mewakili foto yang diambil. Objek ini mengekspos properti Bingkai yang mengembalikan objek CapturedFrame yang mewakili gambar. Peristiwa OptionalReferencePhotoCaptured juga menyediakan objek CapturedFrame dalam argumen peristiwanya. Setelah Anda mendapatkan objek jenis ini, ada sejumlah hal yang dapat Anda lakukan dengannya, termasuk membuat SoftwareBitmap atau menyimpan gambar ke file.

Mendapatkan SoftwareBitmap dari CapturedFrame

Sangat mudah untuk mendapatkan SoftwareBitmap dari objek CapturedFrame hanya dengan mengakses properti SoftwareBitmap objek. Namun, sebagian besar format pengodean tidak mendukung SoftwareBitmap dengan AdvancedPhotoCapture, jadi Anda harus memeriksa dan memastikan properti tidak null sebelum menggunakannya.

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

Dalam rilis saat ini, satu-satunya format pengodean yang mendukung SoftwareBitmap untuk AdvancedPhotoCapture adalah NV12 yang tidak dikompresi. Jadi, jika Anda ingin menggunakan fitur ini, Anda harus menentukan pengodean tersebut saat memanggil PrepareAdvancedPhotoCaptureAsync.

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

Tentu saja, Anda selalu dapat menyimpan gambar ke file dan kemudian memuat file ke dalam SoftwareBitmap dalam langkah terpisah. Untuk informasi selengkapnya tentang bekerja dengan SoftwareBitmap, lihat Membuat, mengedit, dan menyimpan gambar bitmap.

Menyimpan CapturedFrame ke file

Kelas CapturedFrame mengimplementasikan antarmuka IInputStream, sehingga dapat digunakan sebagai input ke BitmapDecoder, dan kemudian BitmapEncoder dapat digunakan untuk menulis data gambar ke disk.

Dalam contoh berikut, folder baru di pustaka gambar pengguna dibuat dan file dibuat dalam folder ini. Perhatikan bahwa aplikasi Anda harus menyertakan kemampuan Pustaka Gambar dalam file manifes aplikasi Anda untuk mengakses direktori ini. Aliran file kemudian dibuka ke file yang ditentukan. Selanjutnya, BitmapDecoder.CreateAsync dipanggil untuk membuat dekoder dari CapturedFrame. Kemudian CreateForTranscodingAsync membuat encoder dari aliran file dan dekoder.

Langkah berikutnya mengodekan orientasi foto ke dalam file gambar dengan menggunakan BitmapProperties dari encoder. Untuk informasi selengkapnya tentang menangani orientasi saat menangkap gambar, lihat Menangani orientasi perangkat dengan MediaCapture.

Akhirnya, gambar ditulis ke file dengan panggilan ke 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;
}