2010-06-30 15 views
22

Estoy tratando de capturar video desde una cámara. He recibido la devolución de llamada captureOutput:didOutputSampleBuffer: para activar y me da un buffer de muestra que luego convierto a CVImageBufferRef. Luego intento convertir esa imagen a UIImage que luego puedo ver en mi aplicación.cómo convertir un CVImageBufferRef a UIImage

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection 
{ 
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    /*Lock the image buffer*/ 
    CVPixelBufferLockBaseAddress(imageBuffer,0); 
    /*Get information about the image*/ 
    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer); 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    /*We unlock the image buffer*/ 
    CVPixelBufferUnlockBaseAddress(imageBuffer,0); 

    /*Create a CGImageRef from the CVImageBufferRef*/ 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    CGImageRef newImage = CGBitmapContextCreateImage(newContext); 

    /*We release some components*/ 
    CGContextRelease(newContext); 
    CGColorSpaceRelease(colorSpace); 

    /*We display the result on the custom layer*/ 
    /*self.customLayer.contents = (id) newImage;*/ 

    /*We display the result on the image view (We need to change the orientation of the image so that the video is displayed correctly)*/ 
    UIImage *image= [UIImage imageWithCGImage:newImage scale:1.0 orientation:UIImageOrientationRight]; 
    self.capturedView.image = image; 

    /*We relase the CGImageRef*/ 
    CGImageRelease(newImage); 
} 

el código parece funcionar bien hasta la llamada a CGBitmapContextCreate. siempre devuelve un puntero NULL. por lo tanto, ninguno de los demás funciona. pase lo que pase, la función devuelve null. no tengo ni idea de porqué.

Respuesta

19

La forma en que está de paso en la baseAddress presume que los datos de imagen está en la forma

ACCC

(donde C es algún componente de color, R || G || B).

Si ha configurado su AVCaptureSession para capturar los marcos de video en formato nativo, es muy probable que esté recuperando los datos de video en el formato plano YUV420. (Consulte: link text) Para hacer lo que está intentando hacer aquí, probablemente lo más fácil sea especificar que desea los marcos de video capturados en kCVPixelFormatType_32RGBA. Apple recomienda que capture los fotogramas de video en kCVPixelFormatType_32BGRA si los captura en formato no plano en absoluto, el razonamiento para el cual no está especificado, pero puedo suponer razonablemente que se debe a consideraciones de rendimiento.

Advertencia: No he hecho esto, y estoy asumiendo que el acceso a los contenidos de CVPixelBufferRef como este es una forma razonable de construir la imagen. No puedo garantizar que esto realmente funcione, pero puedo/decirte que la forma en que estás haciendo las cosas de manera confiable ahora no funcionará debido al formato de píxel en el que probablemente (tal vez) estés capturando los cuadros de video.

+8

completamente la respuesta correcta, por lo que sólo como un comentario: kCVPixelFormatType_32RGBA no está disponible como un formato de captura en el iPhone 4 al menos en iOS 4.2.1 . BGRA está completamente implementado. – Tommy

2

Benjamin Loulier escribió una muy buena publicación sobre la salida de un CVImageBufferRef bajo la consideración de la velocidad con múltiples enfoques.

También se puede encontrar un ejemplo de trabajo en github;)

¿Qué hay de nuevo en el tiempo? ;) Aquí tienes: http://web.archive.org/web/20140426162537/http://www.benjaminloulier.com/posts/ios4-and-direct-access-to-the-camera

+0

Parece que esta publicación de blog se ha ido. – Brigham

+0

@ Brigham - Acabo de actualizar el enlace. De nada. – maaalex

10

Si simplemente necesitas convertir un CVImageBufferRef a UIImage, parece ser mucho más difícil de lo que debería ser. Esencialmente necesitas convertir a CIImage, luego CGImage, THEN UIImage. Desearía poder decirte por qué. Quién sabe.

-(void) screenshotOfVideoStream:(CVImageBufferRef)imageBuffer 
{ 
    CIImage *ciImage = [CIImage imageWithCVPixelBuffer:imageBuffer]; 
    CIContext *temporaryContext = [CIContext contextWithOptions:nil]; 
    CGImageRef videoImage = [temporaryContext 
          createCGImage:ciImage 
          fromRect:CGRectMake(0, 0, 
          CVPixelBufferGetWidth(imageBuffer), 
          CVPixelBufferGetHeight(imageBuffer))]; 

    UIImage *image = [[UIImage alloc] initWithCGImage:videoImage]; 
    [self doSomethingWithOurUIImage:image]; 
    CGImageRelease(videoImage); 
} 

Este método particular trabajó para mí cuando yo estaba en la conversión de vídeo H.264 mediante la devolución de llamada VTDecompressionSession para obtener el CVImageBufferRef (pero debería funcionar para cualquier CVImageBufferRef). Estaba usando iOS 8.1, XCode 6.2.

+2

No necesita todos esos pasos.Puedes llamar a [[UIImage alloc] initWithCIImage ...] –

+1

Intenté hacer eso, pero no funcionó y obtuve una imagen blanca sólida. Por alguna razón, necesitaba el CIContext para hacer esto (como [esta respuesta] (http://stackoverflow.com/a/7788510/3841734)). Quizás era solo mi caso específico que lo necesitaba. Seré honesto. No entiendo completamente todo este formato de imagen y contexto. –

+1

podría querer rodear todo esto en un bloque @autorelease ... – theprojectabot

1

Puede llamar directamente:

self.yourImageView.image=[[UIImage alloc] initWithCIImage:[CIImage imageWithCVPixelBuffer:imageBuffer]]; 
+0

Esto funciona, pero parece ser mucho más lento que el enfoque de Livy Storks –

Cuestiones relacionadas