25 - Control CameraCaptureUI para tomar fotos


La clase CameraCaptureUI es la forma más sencilla para capturar fotos desde una aplicación universal de Windows.

Esta componente visual cuando se la invoca contiene toda la interfaz visual para la captura de fotos.

Problema

Implementar una aplicación que permita tomar una foto y posteriormente la muestre en un control Image.

Como primer paso creamos un nuevo proyecto llamado "Proyecto33" seleccionando desde el menú de opciones del Visual Studio: Archivo -> Nuevo -> Proyecto

La interfaz visual queda definida por el archivo MainPage.xaml que contiene un botón que activa el módulo para tomar la foto y un objeto de la clase Image para mostrarlo:

<Page
    x:Class="Proyecto33.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Proyecto33"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Button Content="Tomar foto" 
                Click="boton1_Click"  
                HorizontalAlignment="Center"
                Grid.Row="0" />
        <Image Grid.Row="1" 
                      x:Name="imagen1" 
                      Stretch="Fill"/>
    </Grid>
</Page>

En código completo C# asociado a esta interfaz visual es:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Graphics.Imaging;
using Windows.Media.Capture;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;

// La plantilla de elemento Página en blanco está documentada en http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace Proyecto33
{
    /// 

    /// Página vacía que se puede usar de forma independiente o a la que se puede navegar dentro de un objeto Frame.
    /// 

    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private async void boton1_Click(object sender, RoutedEventArgs e)
        {
            CameraCaptureUI camara1 = new CameraCaptureUI();
            camara1.PhotoSettings.Format = CameraCaptureUIPhotoFormat.Jpeg;
            camara1.PhotoSettings.CroppedSizeInPixels = new Size(800, 800);
            StorageFile foto1 = await camara1.CaptureFileAsync(CameraCaptureUIMode.Photo);
            if (foto1 != null)
            {

                IRandomAccessStream stream1 = await foto1.OpenAsync(FileAccessMode.Read);
                BitmapDecoder decoder1 = await BitmapDecoder.CreateAsync(stream1);
                SoftwareBitmap softwareBitmap1 = await decoder1.GetSoftwareBitmapAsync();

                SoftwareBitmap softwareBitmapBGR8 = SoftwareBitmap.Convert(softwareBitmap1,
                        BitmapPixelFormat.Bgra8,
                        BitmapAlphaMode.Premultiplied);

                SoftwareBitmapSource bitmapSource1 = new SoftwareBitmapSource();
                await bitmapSource1.SetBitmapAsync(softwareBitmapBGR8);

                imagen1.Source = bitmapSource1;
            }
        }
    }
}

Los espacios de nombres siguientes son necesarios para tomar fotos y luego procesarlas y visualizarlas:

using Windows.Graphics.Imaging;
using Windows.Media.Capture;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media.Imaging;

Cuando se trabajan con archivos, imágenes y en general actividades que requieran mucho tiempo y tienen el potencial de bloquearnos la aplicación se debe implementar métodos asincrónicos mediante la palabra clave async (y luego se llama los métodos antecediendo la palabra clave await):

        private async void boton1_Click(object sender, RoutedEventArgs e)
        {
        }

Definimos y creamos un objeto de la clase CameraCaptureUI:

            CameraCaptureUI camara1 = new CameraCaptureUI();

Configuramos la propiedad Format indicando el tipo de formato de archivo a generar por la componente:

            camara1.PhotoSettings.Format = CameraCaptureUIPhotoFormat.Jpeg;

Configuramos el ancho y alto en píxeles de la foto a tomar:

            camara1.PhotoSettings.CroppedSizeInPixels = new Size(800, 800);

Llamamos al método CaptureFileAsync que nos muestra la interfaz visual para tomar la foto:

            StorageFile foto1 = await camara1.CaptureFileAsync(CameraCaptureUIMode.Photo);

Cuando ejecutamos el programa y presionamos el botón de "Tomar foto" aparece la siguiente interfaz al ejecutar el método CaptureFileAsync:

CameraCaptureUI fotos

Luego de tomar la foto tenemos dos alternativas: confirmarla o rechazarla:

CameraCaptureUI fotos

Si la rechazamos presionando la 'x' el método CaptureFileAsync retorna un valor null por lo que el if siguiente se verifica falso:

            if (foto1 != null)
            {

Pero si confirmamos la foto que acabamos de tomar la variable StorageFile almacena la referencia al archivo con la imagen tomada.

Dentro del if llamamos a OpenAsync para obtener una secuencia del archivo de la imagen, llamamos a BitmapDecoder.CreateAsync para obtener un descodificador de mapa de bits de la secuencia y a continuación realizamos una llamada a GetSoftwareBitmapAsync para obtener un objeto de la clase SoftwareBitmap de la imagen:

               IRandomAccessStream stream1 = await foto1.OpenAsync(FileAccessMode.Read);
                BitmapDecoder decoder1 = await BitmapDecoder.CreateAsync(stream1);
                SoftwareBitmap softwareBitmap1 = await decoder1.GetSoftwareBitmapAsync();

El control Image requiere que el origen de la imagen sea en formato de BGRA8, por lo tanto, realizamos una llamada al método estático SoftwareBitmap.Convert para crear un nuevo mapa de bits de software con el formato deseado. A continuación, crea un objeto SoftwareBitmapSource nuevo y realizamos una llamada a SetBitmapAsync. Por último, establecemos en la propiedad Source del objeto imagen1 la foto capturada en la interfaz de usuario:

                SoftwareBitmap softwareBitmapBGR8 = SoftwareBitmap.Convert(softwareBitmap1,
                        BitmapPixelFormat.Bgra8,
                        BitmapAlphaMode.Premultiplied);

                SoftwareBitmapSource bitmapSource1 = new SoftwareBitmapSource();
                await bitmapSource1.SetBitmapAsync(softwareBitmapBGR8);

                imagen1.Source = bitmapSource1;

Tenemos luego de confirmar la foto en la interfaz de nuestra aplicación la imagen tomada.

Este proyecto lo puede descargar en un zip desde este enlace :Proyecto33.zip

Retornar