2015-03-31 98 views
3

我有臨時變量tmpPixelBuffer與像素緩衝區數據,它不是nil,並且當檢測到元數據對象時,我想從該緩衝區創建圖像,以便我可以從該圖像裁剪元數據圖像。如何將CVImageBuffer轉換爲UIImage?

圖片總是nil,我該怎麼做?

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 capturedImage = UIImage(CGImage: cgiImage) 
    ... 
} 

我也試圖那樣做:

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)))) 
    ... 
} 

但在這種情況下的UIImage無法讀取。

+0

我的猜測是,如果輸出格式不是BGRA,那麼你得到的垃圾回來。只是一個猜測。順便說一句,你爲什麼沒有給你一個給你一個非常好的功能(我知道 - 它的ObjC)的Andrea的答案。 – 2017-06-19 20:42:09

回答

4

我不知道在SWIFT中,但我認爲你可以很容易地轉換,這是從蘋果採取的C函數,並且完美地工作。
使用CIImage的問題在於創建上下文是一項相當昂貴的任務,因此如果您想要這樣做,最好先在所有內容之前構建上下文,並對其保持強有力的參考。
此外,我不記得如果默認上下文是爲GPU或CPU構建的,那麼在2之間還有其他不同的區別。例如,如果您想在GPU上的後臺線程上創建映像,它將無法工作。

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; 
} 
3

我轉換安德烈的回答到雨燕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 
}