2015-03-31 6 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). –

ответ

4

Я не знаю в SWIFT, но я думаю, что вы можете легко преобразовать эту функцию C, которая была взята из Apple и работает отлично.
Проблема, связанная с использованием CIImage, заключается в том, что создание контекста - довольно дорогостоящая задача, поэтому, если вы хотите пойти таким образом, лучше построить контекст перед всем и сохранить для него сильную ссылку.
Кроме того, я не помню, если контекст по умолчанию построен для графического процессора или процессора, есть и другие различия в тонкостях между 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

я преобразовал ответ Андреа в 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 
}