.net, Computer Vision, UWP, Visual Studio, Windows Store Apps

How to use the camera on your device with C# in a UWP application: Part #3, saving a picture

Previously in this series, we looked at how to preview your device’s camera output, and how to use a physical button to focus the camera.

This time I’d like to look at how to capture an image, and store it in a local device folder.

Adding the capability to save to the pictures folder

If you want to save pictures to one of the many standard Windows folders, you need to add this capability to the package manifest. In the VS2015 project which we’ve been building over the last two parts of this series, double click on the Package.appxmanifest file. In the list of capabilities, tick the box with the text “Pictures Library”.

screenshot.1461274147

Our application is now allowed to save to the Pictures library on our device.

Capture an image using the device button

In part 2, we set up our app to make the camera focus when the button is half pressed – after it has focussed, we’d like to fully press the button to capture the image presently being previewed. To do this, we need to handle the CameraPressed event in our code.

if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
{
    HardwareButtons.CameraHalfPressed += HardwareButtons_CameraHalfPressed;
    HardwareButtons.CameraPressed += HardwareButtons_CameraPressed;
}

The next step is to write the event handler.

Writing to “Known Folders”

The Windows UWP API has some functions already baked in that allow us to identify special folders in Windows, and save files to these folders.

To get these special folders, we use the static class “KnownFolders”. For each of these known folders, there are methods available to create files. These created files implement the IStorageFile interface – and fortunately, the _mediaCapture has a method called CapturePhotoToStorageFileAsync, which allows us to save an image to a file which implements this interface. The code below for the event handler shows how it’s done.

private async void HardwareButtons_CameraPressed(object sender, CameraEventArgs e)
{
    // This is where we want to save to.
    var storageFolder = KnownFolders.SavedPictures;
 
    // Create the file that we're going to save the photo to.
    var file = await storageFolder.CreateFileAsync("sample.jpg", CreationCollisionOption.ReplaceExisting);
 
    // Update the file with the contents of the photograph.
    await _mediaCapture.CapturePhotoToStorageFileAsync(ImageEncodingProperties.CreateJpeg(), file);
}

So now we have a basic Windows application, which acts as a viewfinder, allows us to focus if the device is capable, and then allows us to save the presently displayed image to the special Windows SavedPictures folder. This is a pretty good app – and we’ve done it in about 100 lines of code (shown below). Not bad!

using System;
using System.Linq;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Foundation.Metadata;
using Windows.Media.Capture;
using Windows.Media.Devices;
using Windows.Media.MediaProperties;
using Windows.Phone.UI.Input;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
 
namespace BasicCamera
{
    public sealed partial class MainPage : Page
    {
        // Provides functionality to capture the output from the camera
        private MediaCapture _mediaCapture;
 
        public MainPage()
        {
            InitializeComponent();
 
            Application.Current.Resuming += Application_Resuming;
 
            if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
            {
                HardwareButtons.CameraHalfPressed += HardwareButtons_CameraHalfPressed;
                HardwareButtons.CameraPressed += HardwareButtons_CameraPressed;
            }
        }
 
        private async void Application_Resuming(object sender, object o)
        {
            await InitializeCameraAsync();
        }
 
        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            await InitializeCameraAsync();
        }
 
        private async Task InitializeCameraAsync()
        {
            if (_mediaCapture == null)
            {
                // Get the camera devices
                var cameraDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
 
                // try to get the back facing device for a phone
                var backFacingDevice = cameraDevices
                    .FirstOrDefault(c => c.EnclosureLocation?.Panel == Windows.Devices.Enumeration.Panel.Back);
 
                // but if that doesn't exist, take the first camera device available
                var preferredDevice = backFacingDevice ?? cameraDevices.FirstOrDefault();
 
                // Create MediaCapture
                _mediaCapture = new MediaCapture();
 
                // Initialize MediaCapture and settings
                await _mediaCapture.InitializeAsync(
                    new MediaCaptureInitializationSettings
                    {
                        VideoDeviceId = preferredDevice.Id
                    });
 
                // Set the preview source for the CaptureElement
                PreviewControl.Source = _mediaCapture;
 
                // Start viewing through the CaptureElement 
                await _mediaCapture.StartPreviewAsync();
            }
        }
 
        private async void HardwareButtons_CameraHalfPressed(object sender, CameraEventArgs e)
        {
            // test if focus is supported
            if (_mediaCapture.VideoDeviceController.FocusControl.Supported)
            {
                // get the focus control from the _mediaCapture object
                var focusControl = _mediaCapture.VideoDeviceController.FocusControl;
 
                // try to get full range, but settle for the first supported one.
                var focusRange = focusControl.SupportedFocusRanges.Contains(AutoFocusRange.FullRange) ? AutoFocusRange.FullRange : focusControl.SupportedFocusRanges.FirstOrDefault();
 
                // try to get the focus mode for focussing just once, but settle for the first supported one.
                var focusMode = focusControl.SupportedFocusModes.Contains(FocusMode.Single) ? FocusMode.Single : focusControl.SupportedFocusModes.FirstOrDefault();
 
                // now configure the focus control with the range and mode as settings
                focusControl.Configure(
                    new FocusSettings
                    {
                        Mode = focusMode,
                        AutoFocusRange = focusRange
                    });
 
                // finally wait for the camera to focus
                await focusControl.FocusAsync();
            }
        }
 
        private async void HardwareButtons_CameraPressed(object sender, CameraEventArgs e)
        {
            // This is where we want to save to.
            var storageFolder = KnownFolders.SavedPictures;
 
            // Create the file that we're going to save the photo to.
            var file = await storageFolder.CreateFileAsync("sample.jpg", CreationCollisionOption.ReplaceExisting);
 
            // Update the file with the contents of the photograph.
            await _mediaCapture.CapturePhotoToStorageFileAsync(ImageEncodingProperties.CreateJpeg(), file);
        }
    }
}

Of course, there’s still a bit more to be done – this code doesn’t handle resource clean-up, or deal with what happens when the application is suspended or loses focus. We’ll look at that next time.