Cómo convertir CVImageBuffer a UIImage?

Tengo una variable temporal tmpPixelBuffer con datos de búfer de píxeles, que no es nil , y cuando se detectan objects de metadatos, quiero crear una image desde ese búfer, para poder recortar las imágenes de metadatos de esa image.

La image es siempre nil , ¿qué hago mal?

 func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) { tmpPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) } func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) { let image = CIImage(CVPixelBuffer: tmpPixelBuffer) let context = CIContext() let cgiImage = context.createCGImage(image, fromRect: image.extent()) let captunetworkingImage = UIImage(CGImage: cgiImage) ... } 

También traté de hacerlo así:

 func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) { let image = CIImage(CVPixelBuffer: tmpPixelBuffer) let context = CIContext(options: nil) let cgiImage = context.createCGImage(image, fromRect: CGRect(x: 0, y: 0, width: Int(CVPixelBufferGetWidth(tmpPixelBuffer)), height: Int(CVPixelBufferGetHeight(tmpPixelBuffer)))) ... } 

Pero en este caso UIImage no es legible.

No lo sé en SWIFT, pero creo que puedes convertir fácilmente, esta function C que se tomó de Apple y funciona perfectamente.
El problema con CIImage es que crear un context es una tarea bastante costosa, así que si quieres ir por ese path es mejor build el context antes de todo y mantener una fuerte reference a él.
Además, no recuerdo si el context pnetworkingeterminado es compilation para GPU o CPU, hay otras diferencias sutiles entre el 2. Por ejemplo, si desea crear la image en un subprocess de background en la GPU, no funcionará.

 static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; static void ReleaseCVPixelBuffer(void *pixel, const void *data, size_t size) { CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)pixel; CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 ); CVPixelBufferRelease( pixelBuffer ); } // create a CGImage with provided pixel buffer, pixel buffer must be uncompressed kCVPixelFormatType_32ARGB or kCVPixelFormatType_32BGRA static OSStatus CreateCGImageFromCVPixelBuffer(CVPixelBufferRef pixelBuffer, CGImageRef *imageOut) { OSStatus err = noErr; OSType sourcePixelFormat; size_t width, height, sourceRowBytes; void *sourceBaseAddr = NULL; CGBitmapInfo bitmapInfo; CGColorSpaceRef colorspace = NULL; CGDataProviderRef provider = NULL; CGImageRef image = NULL; sourcePixelFormat = CVPixelBufferGetPixelFormatType( pixelBuffer ); if ( kCVPixelFormatType_32ARGB == sourcePixelFormat ) bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipFirst; else if ( kCVPixelFormatType_32BGRA == sourcePixelFormat ) bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst; else return -95014; // only uncompressed pixel formats sourceRowBytes = CVPixelBufferGetBytesPerRow( pixelBuffer ); width = CVPixelBufferGetWidth( pixelBuffer ); height = CVPixelBufferGetHeight( pixelBuffer ); DLog(@"Buffer image size %zu e %zu",width,height ); CVReturn val = CVPixelBufferLockBaseAddress( pixelBuffer, 0 ); if (val == kCVReturnSuccess) { sourceBaseAddr = CVPixelBufferGetBaseAddress( pixelBuffer ); colorspace = CGColorSpaceCreateDeviceRGB(); CVPixelBufferRetain( pixelBuffer ); provider = CGDataProviderCreateWithData( (void *)pixelBuffer, sourceBaseAddr, sourceRowBytes * height, ReleaseCVPixelBuffer); image = CGImageCreate(width, height, 8, 32, sourceRowBytes, colorspace, bitmapInfo, provider, NULL, true, kCGRenderingIntentDefault); } bail: if ( err && image ) { CGImageRelease( image ); image = NULL; } if ( provider ) CGDataProviderRelease( provider ); if ( colorspace ) CGColorSpaceRelease( colorspace ); *imageOut = image; return err; } // utility used by newSquareOverlayedImageForFeatures for static CGContextRef CreateCGBitmapContextForSize(CGSize size) { CGContextRef context = NULL; CGColorSpaceRef colorSpace; int bitmapBytesPerRow; bitmapBytesPerRow = (size.width * 4); colorSpace = CGColorSpaceCreateDeviceRGB(); context = CGBitmapContextCreate (NULL, size.width, size.height, 8, // bits per component bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast); CGContextSetAllowsAntialiasing(context, NO); CGColorSpaceRelease( colorSpace ); return context; } 

Convertí la respuesta de Andrea en Swift 3.1:

 static func DegreesToRadians(_ degrees: CGFloat) -> CGFloat { return CGFloat( (degrees * .pi) / 180 ) } static func CreateCGImageFromCVPixelBuffer(pixelBuffer: CVPixelBuffer) -> CGImage? { let bitmapInfo: CGBitmapInfo let sourcePixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer) if kCVPixelFormatType_32ARGB == sourcePixelFormat { bitmapInfo = [.byteOrder32Big, CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue)] } else if kCVPixelFormatType_32BGRA == sourcePixelFormat { bitmapInfo = [.byteOrder32Little, CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue)] } else { return nil } // only uncompressed pixel formats let sourceRowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer) let width = CVPixelBufferGetWidth(pixelBuffer) let height = CVPixelBufferGetHeight(pixelBuffer) print("Buffer image size \(width) height \(height)") let val: CVReturn = CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) if val == kCVReturnSuccess, let sourceBaseAddr = CVPixelBufferGetBaseAddress(pixelBuffer), let provider = CGDataProvider(dataInfo: nil, data: sourceBaseAddr, size: sourceRowBytes * height, releaseData: {_,_,_ in }) { let colorspace = CGColorSpaceCreateDeviceRGB() let image = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: sourceRowBytes, space: colorspace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent) CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0)) return image } else { return nil } } // utility used by newSquareOverlayedImageForFeatures for static func CreateCGBitmapContextForSize(_ size: CGSize) -> CGContext? { let bitmapBytesPerRow = Int(size.width * 4) let colorSpace = CGColorSpaceCreateDeviceRGB() guard let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: bitmapBytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return nil } context.setAllowsAntialiasing(false) return context } 
    Intereting Posts