来自相机(如“645 PRO”)的原始图像数据

12

以前我已经问过这个问题,并且我也得到了一个好答案:

我在这个论坛上搜索了很久,但是我没有找到我真正需要的东西。我想从相机中获取原始图像数据。到目前为止,我试图从captureStillImageAsynchronouslyFromConnection:completionHandler:方法中的imageDataSampleBuffer中获取数据,并将其写入NSData对象,但那行不通。也许我走错了路,或者我只是做错了。我不希望图像以任何方式被压缩。

简单的方法是使用AVCaptureStillImageOutput中的jpegStillImageNSDataRepresentation:,但是像我说的,我不希望它被压缩。

谢谢!

相机原始图像数据

我以为我可以用它来工作,但最终我注意到我需要以类似于“645 PRO”中所做的方式更直接地获取原始图像数据。

645 PRO:RAW Redux

该网站上的图片显示他们在进行任何jpeg压缩之前获得了原始数据。这就是我想要做的。我的猜想是我需要转换imageDataSampleBuffer,但是我没有看到完全不压缩它的方法。 “645 PRO”还将其图片保存为TIFF,因此我认为它使用了至少一个其他库。

我不想制作照片应用程序,但我需要最好的质量来检查图片中的某些特征。

谢谢!

编辑1: 所以经过一段时间的尝试和搜索不同方向,我决定进行状态更新。

该项目的最终目标是检查图片中的某些特征,这将在opencv的帮助下完成。但在应用程序能够在手机上执行之前,我正在尝试从手机中获取大多未压缩的图像以在计算机上分析它们。

因此,我想将Brad Larson代码获得的“包含来自相机的未压缩BGRA字节的NSData实例”保存为bmp或TIFF文件。正如我在评论中所说,我尝试使用opencv进行此操作(无论如何都会需要),但我最好的成果是使用Computer Vision Talks中的函数将其转换为UIImage。

void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
cv::Mat frame(height, width, CV_8UC4, (void*)baseAddress);
UIImage *testImag = [UIImage imageWithMat:frame andImageOrientation:UIImageOrientationUp];
//imageWithMat... being the function from Computer Vision Talks which I can post if someone wants to see it

ImageMagick - 方法

另一种我尝试过的方法是使用ImageMagick,正如在另一个帖子中所建议的那样。 但是我没有找到不使用像UIImagePNGRepresentationUIImageJPEGRepresentation这样的东西即可完成它的方法。

现在我正在尝试使用该教程来使用libtiff做些事情。

也许有人有想法或知道更简单的方法将我的缓冲区对象转换为未压缩的图片。 再次感谢您的帮助!

编辑2:

我发现了一些东西!而且我必须说我真的很糊涂。

void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
cv::Mat frame(height, width, CV_8UC4, (void*)baseAddress);

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"ocv%d.TIFF", picNum]];
const char* cPath = [filePath cStringUsingEncoding:NSMacOSRomanStringEncoding];

const cv::string newPaths = (const cv::string)cPath;

cv::imwrite(newPaths, frame);

我只需要使用OpenCV的imwrite函数即可。这样,在进行Beyer-Polarisation后,我就可以直接得到大约30MB的TIFF文件!


1
我在大约10秒钟的搜索中找到了这个答案:https://dev59.com/amgv5IYBdhLWcg3wD8yY#10865206 - David H
1
谢谢Dustin,我已经担心会出现这样的情况了,只是希望有人能给我一点提示或者什么的。还有感谢David H,但我已经看过了,对我的情况并没有太大帮助,至少我没能弄清楚怎么做。不过,我对每一个提示都非常感激! - thomketler
4个回答

17

哇,那篇博客真是特别。为了说明他们从静态图像返回的样本缓冲区字节,要用很多的话。他们的方法并没有什么特别创新的地方,我知道有一些相机应用程序也这么做。

使用以下代码可以获取AVCaptureStillImageOutput拍摄的照片返回的原始字节:

[photoOutput captureStillImageAsynchronouslyFromConnection:[[photoOutput connections] objectAtIndex:0] completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
    CVImageBufferRef cameraFrame = CMSampleBufferGetImageBuffer(imageSampleBuffer);
    CVPixelBufferLockBaseAddress(cameraFrame, 0);
    GLubyte *rawImageBytes = CVPixelBufferGetBaseAddress(cameraFrame);
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(cameraFrame);
    NSData *dataForRawBytes = [NSData dataWithBytes:rawImageBytes length:bytesPerRow * CVPixelBufferGetHeight(cameraFrame)];
    // Do whatever with your bytes

    CVPixelBufferUnlockBaseAddress(cameraFrame, 0);
}];

这将为您提供一个NSData实例,其中包含从相机返回的未压缩BGRA字节。您可以将它们保存到磁盘上或者对它们进行任何操作。如果您真正需要处理这些字节本身,我建议避免创建NSData的开销,而是直接使用来自像素缓冲区的字节数组。


非常感谢您的帮助。很高兴知道我并没有完全走错路。希望我没有让您失去耐心,但是您是否知道将此转换为位图的简单方法?我考虑使用openCV以类似于另一篇文章链接的方式进行操作,只是想知道是否有更简单的方法。再次感谢您,我已经遇到了这个问题很长时间了。 - thomketler

5
我可以使用OpenCV解决它。感谢所有帮助过我的人。
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
cv::Mat frame(height, width, CV_8UC4, (void*)baseAddress);

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"ocv%d.BMP", picNum]];
const char* cPath = [filePath cStringUsingEncoding:NSMacOSRomanStringEncoding];

const cv::string newPaths = (const cv::string)cPath;

cv::imwrite(newPaths, frame);

我只需要使用opencv的imwrite函数。这样,在bayer滤波后,直接得到约24MB大小的BMP文件!


2

回答的核心来自iOS:从相机获取逐像素数据中的Brad,但是关键因素在Brad的回复中完全不清楚。 它隐藏在“一旦您配置了捕获会话…”中。

您需要为AVCaptureStillImageOutput设置正确的outputSettings。

例如,将kCVPixelBufferPixelFormatTypeKey设置为kCVPixelFormatType_420YpCbCr8BiPlanarFullRange将为您提供一个YCbCr imageDataSampleBuffer,您可以在captureStillImageAsynchronouslyFromConnection:completionHandler:中操纵它,以您想要的方式操作。


2
需要注意的一点是:kCVPixelBufferPixelFormatTypeKey 是一个整数,因此在添加到字典中时必须将其包装在 NSNumber 中。此外,在 ARC 下,您还需要手动将实际值转换为 id - FeifanZ

0

正如@Wildaker所提到的,为了让特定代码工作,您必须确定相机发送给您哪种像素格式。如果将代码设置为32位RGBA格式,则@thomketler的代码将起作用。

这是一个使用OpenCV的相机默认YUV代码:

cv::Mat convertImage(CMSampleBufferRef sampleBuffer)
{
    CVImageBufferRef cameraFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
    CVPixelBufferLockBaseAddress(cameraFrame, 0);

    int w = (int)CVPixelBufferGetWidth(cameraFrame);
    int h = (int)CVPixelBufferGetHeight(cameraFrame);
    void *baseAddress = CVPixelBufferGetBaseAddressOfPlane(cameraFrame, 0);

    cv::Mat img_buffer(h+h/2, w, CV_8UC1, (uchar *)baseAddress);
    cv::Mat cam_frame;
    cv::cvtColor(img_buffer, cam_frame, cv::COLOR_YUV2BGR_NV21);
    cam_frame = cam_frame.t();

    //End processing
    CVPixelBufferUnlockBaseAddress( cameraFrame, 0 );

    return cam_frame;
}

cam_frame 应该包含完整的 BGR 帧。希望这有所帮助。


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