sábado, 26 de enero de 2013

Realidad Aumentada - Parte II - Captura de Cámara y Video

Guardando imágenes en diferentes formatos
UIKit incluye algunas funciones de C que nos permiten exportar imágenes a archivos en cualquier dispositivo de iOS. Los formatos más comunes son JPEG y PNG. Abrimos PhotoViewController.m en Xcode. Al final del método didFinishPickingMediaWithInfo agregamos lo siguiente:
// guardamos los archivos
NSString *pngPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/ConvertedPNG.png"];
NSString *jpgPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/ConvertedJPEG.jpg"];
[UIImageJPEGRepresentation(image, 1.0) writeToFile:jpgPath atomically:YES]; [UIImagePNGRepresentation(image) writeToFile:pngPath atomically:YES];
// optional (check for files)
//NSError *error;
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *documentsDirectory = [NSHomeDirectory()
stringByAppendingPathComponent:@"Documents"];
NSLog(@"Documents: %@", [fileMgr contentsOfDirectoryAtPath:documentsDirectory
error:&error]);
Primero definimos donde esta ubicados los 2 archivos. Luego usamos funciones de C (UIImageJPEGRepresentation and UIImagePNGRepresentation) para escribir los archivos. Como parte de los parametros pasamos la calidad de compresión medida en una escala de 0.0 a 1.0 . Vemos que guardamos las imágenes con la mejor calidad posible y la menor compresión.

Captura de Video
Hay dos formas de hacer una captura en vivo de video. Una de las formas no requiere analizar los frames de video. Esto se ve en las aplicaciones de realidad aumentada en donde se utilizan clases que nos dicen la ubicación y el giroscopio para determinar donde estamos ubicados. En estas apps vemos el video y sobre este información sobre la ubicación quedando todo integrado. La otra forma, como es en el caso del reconocimiento facial, requiere que analicemos cada frame.

Creando una base de video preview
Abrimos en XCode PhotoViewController.m y buscamos el método loadPhotoPicker. Este método nos permite tomar una foto, ahora lo actualizamos con el siguiente código:
 UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init]; 
    imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
    // uncomment for front camera
    //imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;   imagePicker.cameraDevice = UIImagePickerControllerCameraCaptureModeVideo;  imagePicker.showsCameraControls = NO;
    imagePicker.toolbarHidden = YES;
    imagePicker.navigationBarHidden = YES;
    imagePicker.wantsFullScreenLayout = YES;
    imagePicker.delegate = self;
    imagePicker.allowsEditing = NO;
    [self presentModalViewController:imagePicker animated:YES];
Seteamos unas opciones para el objeto UIImagePickerController. Primero configuramos el dispositivo para el tipo de camera de video. Luego renderizamos UIImagePickerController en modo full-screen. Esta interfaz de video provee un background perfecto para crear una aplicación de realidad aumentada.
Si quisieramos analizar estos frames de videos deberiamos setear un NSTimer y capturar y guardar cada preview sin embargo esto no sería lo ideal en tiempo de procesador. Analizaremos mejores formas de captura de video frames.
Si lo ejecutan van a ver una imagen similar a esta:


Este video es un fondo perfecto para realizar una aplicación de realidad aumentada. Como dijimos antes realizar un NSTimer no es la mejor forma de analizar los frames, vamos a ver que forma es la ideal.
Para la captura de frames por sesiones vamos a utilizar una clase llamada AVCapturesSession. Vamos a hacerlo en un tab separado para ver bien las diferencias.
Lo primero que tenemos que hacer es agregar unos frameworks para poder trabajar con la libreria AVFoundation, estos son los que necesitamos:

CoreVideo
CoreMedia
AVFoundation
ImageIO

Creamos un nuevo tab bar item como vemos en la siguiente imagen:

Creamos un nuevo controlodar llamado ARCVideoViewController para este nuevo item que agregamos.
#import 
#import  
#import 

 @interface VideoViewController : UIViewController {
 }

 @property (strong, nonatomic) IBOutlet UIView *videoPreview;
 @end

Luego agregamos dos oulets más, un UIImageView y un UIButton al ARCVideoViewController. Al UIImageView lo llamamos videoImage y conectamos una acción que la llamaremos captureScreen al UIButton.
Para guardar una imagen desde un preview de un video utilizaremos un objeto del tipo AVCaptureStillImageOutput.
Creamos una propiedad para este objeto a la cual llamaremos stillImageOutput.
 
 VideoViewController.h debería quedar de esta manera:
 #import 
 #import  
 #import 
 @interface VideoViewController : UIViewController { }
 @property(nonatomic, retain) AVCaptureStillImageOutput *stillImageOutput;
 @property (strong, nonatomic) IBOutlet UIView *videoPreview;
 @property (strong, nonatomic) IBOutlet UIImageView *videoImage;
- (IBAction)captureScreen:(id)sender;
En VideoViewController.m necesitamos crear un método viewDidAppear para comenzar la sesión preview de la cámara. Creamos el método viewDidAppear
 
 - (void)viewDidAppear:(BOOL)animated {
     AVCaptureSession *session = [[AVCaptureSession alloc] init]; 
     session.sessionPreset = AVCaptureSessionPresetMedium;
     AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
     captureVideoPreviewLayer.frame = self.videoPreview.bounds; 
     [self.videoPreview.layer addSublayer:captureVideoPreviewLayer];
     AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
     NSError *error = nil;
     AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
 }
 if (!input) {
     NSLog(@"ERROR: trying to open camera: %@", error);
 }
 [session addInput:input]; // Placeholder
 [session startRunning];
 
}
Lo primero que hicimos en este método fue crear una instancia de AVCaptureSession, seteamos la calidad de video a media y creamos una instancia de AVCaptureVideoPreviewLayer. Esto se usara para almacenar en el buffer el preview.
Luego seteamos el frame para rellenar el tamaño del video preview. Luego configuramos AVCaptureDevice y verificamos si esta disponible. Si no enviamos el mensaje de startRunning a AVCaptureSession en nuestra view no aparecera nada.
Si ejecutamos la aplicación no vemos ningún cambio interesante por eso lo que vamos a hacer es agregar un botón que al hacer touch capture una imagen.
Agregamos el siguiente código en el método viewDidAppear VideoViewController.m entre [session addInput:input]; y [session startRunning];
 

stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
AVVideoCodecJPEG, AVVideoCodecKey, nil]; 
[stillImageOutput setOutputSettings:outputSettings];
[session addOutput:stillImageOutput];
Hay dos bloques principales en este método. En el primer bloque chequeamos si hay salida de video y seteamos una referencia local para la instancia de AVCaptureConnection.

Luego capturamos asincronicamente la imagen desde la cámara. Seteamos un buffer para almacenar la imagen. Después de esto usamos el framework ImageIO para capturar los adjuntos EXIF. Esto puede ser util para configurar filtros o almacenar valores acerca de la calidad de la foto.
Finalmente configuramos el oulet UIImageView para mostrar el frame de lo que capturamos. Si ejecutan el proyecto deberian ver algo similar a la siguiente imagen:
Código de esta primera parte Realidad Aumentada Parte 2

No hay comentarios: