Rentang dinamis tinggi (HDR) dan pengambilan foto cahaya rendah

Artikel ini menunjukkan kepada Anda cara menggunakan kelas AdvancedPhotoCapture untuk menangkap foto rentang dinamis tinggi (HDR). API ini juga memungkinkan Anda untuk mendapatkan bingkai referensi dari pengambilan 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 memiliki LowLagMediaRecording dan AdvancedPhotoCapture yang disiapkan 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 ditembakkan. 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 dalam adegan saat ini. Pada rilis sebelumnya, pengaturan lampu kilat AdvancedPhotoCapture selalu menimpa pengaturan FlashControl.Enabled .

Catatan

Artikel ini didasarkan pada 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. Sebaiknya biasakan diri Anda dengan pola pengambilan media dasar dalam artikel itu sebelum beralih ke skenario pengambilan yang lebih maju. Kode dalam artikel ini mengasumsikan bahwa aplikasi Anda sudah memiliki instance MediaCapture yang telah diinsialisasi 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 Camera Advanced Capture.

Ruang nama pengambilan foto tingkat lanjut

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

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

Pengambilan foto HDR

Menentukan apakah pengambilan foto HDR didukung pada perangkat saat ini

Teknik pengambilan HDR yang dijelaskan dalam artikel ini dilakukan menggunakan objek AdvancedPhotoCapture . Tidak semua perangkat mendukung pengambilan HDR dengan AdvancedPhotoCapture. Tentukan apakah perangkat tempat aplikasi Anda saat ini berjalan mendukung teknik ini 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 perlu mengakses instans AdvancedPhotoCapture dari beberapa tempat dalam kode Anda, Anda harus mendeklarasikan variabel anggota untuk menahan objek.

private AdvancedPhotoCapture _advancedCapture;

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

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

PrepareAdvancedPhotoCaptureAsync mengembalikan objek AdvancedPhotoCapture yang akan Anda gunakan untuk memulai pengambilan foto. Anda dapat menggunakan objek ini untuk mendaftarkan penangan untuk OptionalReferencePhotoCaptured dan AllPhotosCaptured yang dibahas kemudian dalam 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 Frame-nya .

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 menyandikan 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 Tangani orientasi perangkat dengan MediaCapture.

Metode pembantu SaveCapturedFrameAsync , yang menyimpan gambar ke disk, dibahas kemudian dalam artikel ini.

Dapatkan bingkai referensi opsional

Proses HDR menangkap beberapa frame dan kemudian menggabungkannya menjadi satu gambar setelah semua frame telah ditangkap. Anda bisa mendapatkan akses ke bingkai setelah ditangkap tetapi sebelum seluruh proses HDR selesai dengan menangani acara OptionalReferencePhotoCaptured . Anda tidak perlu melakukan ini jika Anda hanya tertarik dengan 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 acara ini tidak dinaikkan.

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

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

Buat instance baru dari objek konteks Anda, isi anggotanya, lalu berikan 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 penangan peristiwa OptionalReferencePhotoCaptured , lemparkan properti Konteks objek OptionalReferencePhotoCapturedEventArgs ke kelas objek konteks Anda. Contoh ini memodifikasi nama file untuk membedakan gambar bingkai referensi dari gambar HDR akhir dan kemudian 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 frame telah ditangkap

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

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

Bersihkan objek AdvancedPhotoCapture

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

await _advancedCapture.FinishAsync();
_advancedCapture = null;

Pengambilan foto cahaya rendah

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

Sebelum menggunakan pengambilan foto cahaya rendah, 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, deklarasikan variabel anggota untuk menyimpan objek AdvancedPhotoCapture .

private AdvancedPhotoCapture _advancedCapture;

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

Panggil Objek MediaCapturePrepareAdvancedPhotoCaptureAsync, melewati objek ImageEncodingProperties yang menentukan jenis pengkodean yang harus digunakan oleh 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 Tangani orientasi perangkat dengan MediaCapture.

Metode pembantu SaveCapturedFrameAsync , yang menyimpan gambar ke disk, dibahas kemudian dalam artikel ini.

Anda dapat menangkap beberapa foto cahaya rendah tanpa mengkonfigurasi 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. Acara OptionalReferencePhotoCaptured juga menyediakan objek CapturedFrame dalam args acaranya. Setelah Anda mendapatkan objek jenis ini, ada beberapa hal yang dapat Anda lakukan dengannya, termasuk membuat SoftwareBitmap atau menyimpan gambar ke file.

Dapatkan SoftwareBitmap dari CapturedFrame

Sangat sepele untuk mendapatkan SoftwareBitmap dari objek CapturedFrame hanya dengan mengakses properti SoftwareBitmap dari objek. Namun, sebagian besar format pengkodean 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 pengkodean yang mendukung SoftwareBitmap untuk AdvancedPhotoCapture adalah NV12 yang tidak terkompresi. Jadi, jika Anda ingin menggunakan fitur ini, Anda harus menentukan pengkodean itu saat Anda 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 di dalam folder ini. Perhatikan bahwa aplikasi Anda harus menyertakan kemampuan Perpustakaan Gambar dalam file manifes aplikasi Anda untuk mengakses direktori ini. Aliran file kemudian dibuka ke file yang ditentukan. Selanjutnya, BitmapDecoder.CreateAsync dipanggil untuk membuat decoder dari CapturedFrame. Kemudian CreateForTranscodingAsync membuat encoder dari aliran file dan decoder.

Langkah selanjutnya menyandikan orientasi foto ke dalam file gambar dengan menggunakan BitmapProperties encoder. Untuk informasi selengkapnya tentang orientasi penanganan saat mengambil 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;
}