不压缩地捕获静态UIImage(从CMSampleBufferRef)?

3

我需要从CMSampleBufferRef中的未压缩图像数据获取UIImage。我使用以下代码:

captureStillImageOutput captureStillImageAsynchronouslyFromConnection:connection
 completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) 
{
    // that famous function from Apple docs found on a lot of websites
    // does NOT work for still images
    UIImage *capturedImage = [self imageFromSampleBuffer:imageSampleBuffer]; 
}
http://developer.apple.com/library/ios/#qa/qa1702/_index.html 是一个指向imageFromSampleBuffer函数的链接。
但是它不能正常工作。:(
有一个jpegStillImageNSDataRepresentation:imageSampleBuffer方法,但它只提供了压缩数据(因为是JPEG)。
我怎样才能获得用于捕获静态图像后创建的最原始的非压缩数据的UIImage
也许,我应该为视频输出指定一些设置?我目前正在使用以下设置:
captureStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
captureStillImageOutput.outputSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };

我注意到,输出的AVVideoCodecKey有一个默认值,即AVVideoCodecJPEG。有没有什么方法可以避免它呢?或者在捕捉静态图像时是否重要?
我在这里找到了一些内容:Raw image data from camera like "645 PRO",但我只需要一个UIImage,不使用OpenCV、OGLES或其他第三方库。

这里在SO上有很多类似的问题和解决方案。 - Jeepston
@Jeepston 没有找到适用于静态图像的有效解决方案... - dreamzor
2个回答

6
方法imageFromSampleBuffer是有效的,事实上我正在使用修改后的版本,但如果我记得正确,您需要正确设置outputSettings。我认为您需要将键设置为kCVPixelBufferPixelFormatTypeKey,值设置为kCVPixelFormatType_32BGRA
例如:
NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey;                                 
NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];                
NSDictionary* outputSettings = [NSDictionary dictionaryWithObject:value forKey:key];

[newStillImageOutput setOutputSettings:outputSettings];

编辑

我使用这些设置拍照而不是录视频。 你的sessionPreset是AVCaptureSessionPresetPhoto吗?可能会有问题。

AVCaptureSession *newCaptureSession = [[AVCaptureSession alloc] init];
[newCaptureSession setSessionPreset:AVCaptureSessionPresetPhoto];

编辑2

关于将其保存为UIImage的部分与文档中的部分完全相同。这就是我问其他问题来源的原因,但我想那只是在试图摸索。我知道另一种方法,但那需要OpenCV。

- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer{
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

CVPixelBufferLockBaseAddress(imageBuffer, 0);

void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);

// Get the number of bytes per row for the pixel buffer
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// Get the pixel buffer width and height
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);



// Create a device-dependent RGB color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

// Create a bitmap graphics context with the sample buffer data
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8,
                                             bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
// Create a Quartz image from the pixel data in the bitmap graphics context
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
// Unlock the pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer,0);


// Free up the context and color space
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);

// Create an image object from the Quartz image
UIImage *image = [UIImage imageWithCGImage:quartzImage];

// Release the Quartz image
CGImageRelease(quartzImage);

return (image);

}

很抱歉,我猜我的回答对您没有帮助。我不了解足够的信息来考虑您问题的其他可能原因。


你是否使用imageFromSimpleBuffer获取静态图片而不是视频?如果是,请在你的回答中添加你已更改的实现,谢谢!我的设置与你的完全相同,请参见问题中的第二个代码块 :) - dreamzor
对不起,我应该更仔细地阅读你的问题。我已经编辑了我的答案。 - thomketler
谢谢,当然是这样的预设等等。你能否请发一下你的 imageFromSimpleBuffer 实现,这样我就可以试一下? - dreamzor
抱歉,我认为那并不能帮助到你。 - thomketler
1
成功了!但是它的方向不对 :( 不过我会自己解决的,谢谢! - dreamzor
有没有人可以提供一些示例项目或示例代码帮忙?我似乎无法让它工作。我可以创建一个存储库,其中包含我目前拥有的内容,但请至少回复一下,这样我就知道有人看到了。 - Bogus

1

Here's a more efficient way:

UIImage *image = [UIImage imageWithData:[self imageToBuffer:sampleBuffer]];

- (NSData *) imageToBuffer:(CMSampleBufferRef)source {
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(source);
    CVPixelBufferLockBaseAddress(imageBuffer,0);

    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
    size_t width = CVPixelBufferGetWidth(imageBuffer);
    size_t height = CVPixelBufferGetHeight(imageBuffer);
    void *src_buff = CVPixelBufferGetBaseAddress(imageBuffer);

    NSData *data = [NSData dataWithBytes:src_buff length:bytesPerRow * height];

    CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
    return data;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接