iphone - ¿UIImage creado desde CMSampleBufferRef no se muestra en UIImageView?
ios4 avfoundation (5)
Estoy intentando mostrar un UIImage en tiempo real desde la cámara, y parece que mi UIImageView no está mostrando la imagen correctamente. Este es el método que debe implementar AVCaptureVideoDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
// Create a UIImage from the sample buffer data
UIImage *theImage = [self imageFromSampleBuffer:sampleBuffer];
// NSLog(@"Got an image! %f %f", theImage.size.width, theImage.size.height);
// NSLog(@"The image view is %@", imageView);
// UIImage *theImage = [[UIImage alloc] initWithData:[NSData
// dataWithContentsOfURL:[NSURL
// URLWithString:@"http://farm4.static.flickr.com/3092/2915896504_a88b69c9de.jpg"]]];
[self.session stopRunning];
[imageView setImage: theImage];
}
Para quitar los problemas fáciles del camino:
- Usar UIImagePickerController no es una opción (eventualmente haremos cosas con la imagen)
- Sé que se está llamando al controlador (se realizan las llamadas NSLog y veo el resultado)
- Sé que tengo las declaraciones de IBOutlet configuradas correctamente. Si uso el código comentado anteriormente para cargar una imagen arbitraria de la web en lugar de simplemente enviar
setImage:theImage
a imageView, la imagen se carga correctamente (y la segunda llamada a NSLog informa de un objeto no nulo). - Al menos en una medida básica, la imagen que obtengo de
imageFromSampleBuffer:
está bien, ya que NSLog informa que el tamaño es 360x480, que es el tamaño que esperaba.
El código que estoy usando es el fragmento AVFoundation
de Apple recientemente publicado disponible here .
En particular, ese es el código que uso que configura el objeto AVCaptureSession
y sus amigos (de los cuales entiendo muy poco), y crea el objeto UIImage a partir de los buffers de Core Video (ese es el método imageFromSampleBuffer
).
Finalmente, puedo hacer que la aplicación se bloquee si trato de enviar drawInRect:
a una subclase UIView con el UIImage
devuelto por imageFromSamplerBuffer
, mientras que no se imageFromSamplerBuffer
si uso un UIImage
desde una URL como la anterior. Aquí está el seguimiento de la pila del depurador dentro de la caída (me sale una señal EXC_BAD_ACCESS):
#0 0x34a977ee in decode_swap ()
#1 0x34a8f80e in decode_data ()
#2 0x34a8f674 in img_decode_read ()
#3 0x34a8a76e in img_interpolate_read ()
#4 0x34a63b46 in img_data_lock ()
#5 0x34a62302 in CGSImageDataLock ()
#6 0x351ab812 in ripc_AcquireImage ()
#7 0x351a8f28 in ripc_DrawImage ()
#8 0x34a620f6 in CGContextDelegateDrawImage ()
#9 0x34a61fb4 in CGContextDrawImage ()
#10 0x321fd0d0 in -[UIImage drawInRect:blendMode:alpha:] ()
#11 0x321fcc38 in -[UIImage drawInRect:] ()
EDITAR: Aquí hay más información sobre el UIImage que devuelve ese bit de código.
Usando el método descrito here , puedo llegar a los píxeles e imprimirlos, y se ven bien a primera vista (cada valor en el canal alfa es 255, por ejemplo). Sin embargo, hay algo un poco apagado con los tamaños de búfer. La imagen que obtengo de Flickr de esa URL es 375x500, y su [pixelData length]
me da 750000 = 375*500*4
, que es el valor esperado. Sin embargo, los datos de píxeles de la imagen devueltos desde imageFromSampleBuffer:
tienen un tamaño 691208 = 360*480*4 + 8
, por lo que hay 8 bytes adicionales en los datos de píxeles. CVPixelBufferGetDataSize
devuelve este valor de off-by-8. Por un momento pensé que podría deberse a la asignación de búferes en posiciones alineadas en la memoria, pero 691200 es un múltiplo de 256, por lo que tampoco lo explica. Esta discrepancia de tamaño es la única diferencia que puedo distinguir entre los dos UIImages, y podría estar causando el problema. Sin embargo, no hay razón para asignar memoria adicional para el búfer, ya que podría causar una infracción EXC_BAD_ACCESS.
Muchas gracias por cualquier ayuda, y avíseme si necesita más información.
Ben Loulier tiene un buen escrito sobre cómo hacer esto.
Estoy usando su aplicación de ejemplo como punto de partida, y está funcionando para mí. Junto con la sustitución de la función imageFromSamplerBuffer
con algo que crea CGImageRef con CGBitmapContextCreate, está usando la cola de envío principal (a través de dispatch_get_main_queue()
) al configurar el delegado del búfer de muestra de salida. Esta no es la mejor solución porque necesita una cola en serie, y por lo que entiendo, la cola principal no es una cola en serie. Entonces, aunque no está garantizado que los marcos estén en el orden correcto, hasta ahora parece funcionar para mí :)
La captura en vivo de cuadros de video ahora está bien explicada por la Q&A técnica QA1702 de Apple:
https://developer.apple.com/library/ios/#qa/qa1702/_index.html
Otra cosa a tener en cuenta es si realmente está actualizando su UIImageView
en el hilo principal: si no lo está, es probable que no refleje ningún cambio.
captureOutput:didOutputSampleBuffer:fromConnection
método de delegado captureOutput:didOutputSampleBuffer:fromConnection
menudo se llama en un subproceso en segundo plano. Así que quieres hacer esto:
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
CFRetain(sampleBuffer);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//Now we''re definitely on the main thread, so update the imageView:
UIImage *capturedImage = [self imageFromSampleBuffer:sampleBuffer];
//Display the image currently being captured:
imageView.image = capturedImage;
CFRelease(sampleBuffer);
}];
}
También es importante establecer el formato de salida correcto. Tuve un problema con la captura de imágenes cuando utilizaba la configuración de formato predeterminada. Debería ser:
[videoDataOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]];
Tuve el mismo problema ... ¡pero encontré este post anterior, y su método de creación de CGImageRef funciona!
http://forum.unity3d.com/viewtopic.php?p=300819
Aquí hay una muestra de trabajo:
app has a member UIImage theImage;
// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
//... just an example of how to get an image out of this ...
CGImageRef cgImage = [self imageFromSampleBuffer:sampleBuffer];
theImage.image = [UIImage imageWithCGImage: cgImage ];
CGImageRelease( cgImage );
}
- (CGImageRef) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer // Create a CGImageRef from sample buffer data
{
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(imageBuffer,0); // Lock the image buffer
uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); // Get information of the image
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGImageRef newImage = CGBitmapContextCreateImage(newContext);
CGContextRelease(newContext);
CGColorSpaceRelease(colorSpace);
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
/* CVBufferRelease(imageBuffer); */ // do not call this!
return newImage;
}