将 NSData 转换为 CGImage 然后再转换回 NSData 会使文件太大 [英] Converting NSData to CGImage and then back to NSData makes the file too big
问题描述
我使用 AVFoundation
构建了一个相机.
I have built a camera using AVFoundation
.
一旦我的 AVCaptureStillImageOutput
完成了它的 captureStillImageAsynchronouslyFromConnection:completionHandler:
方法,我就会像这样创建一个 NSData 对象:
Once my AVCaptureStillImageOutput
has completed its captureStillImageAsynchronouslyFromConnection:completionHandler:
method, I create a NSData object like this:
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
一旦我有了 NSData
对象,我想旋转图像 - 无需 - 转换为 UIImage
.我发现我可以转换为 CGImage
来这样做.
Once I have the NSData
object, I would like to rotate the image -without- converting to a UIImage
. I have found out that I can convert to a CGImage
to do so.
获得 imageData 后,我开始转换为 CGImage 的过程,但我发现 CGImageRef
最终比 NSData
对象大三十倍.
After I have the imageData, I start the process of converting to CGImage, but I have found that the CGImageRef
ends up being THIRTY times larger than the NSData
object.
这是我用来从 NSData
转换为 CGImage
的代码:
Here is the code I use to convert to CGImage
from NSData
:
CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)(imageData));
CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);
如果我尝试 NSLog
输出图像的大小,当 NSData
是 1.5-2 兆字节的图像时,它会达到 30 兆字节!
If I try to NSLog
out the size of the image, it comes to 30 megabytes when the NSData
was a 1.5-2 megabyte image!
size_t imageSize = CGImageGetBytesPerRow(imageRef) * CGImageGetHeight(imageRef);
NSLog(@"cgimage size = %zu",imageSize);
我想也许当你从 NSData 转到 CGImage 时,图像会解压缩,然后如果我转换回 NSData,它可能会恢复到正确的文件大小.
I thought that maybe when you go from NSData to CGImage, the image decompresses, and then maybe if I converted back to NSData, that it might go back to the right file size.
imageData = (NSData *) CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(imageRef)));
上面的NSData
与CGImageRef
对象具有相同的length
.
The above NSData
has the same length
as the CGImageRef
object.
如果我尝试保存图像,图像是无法打开的 30mb 图像.
If I try to save the image, the image is a 30mb image that cannot be opened.
我对使用 CGImage 完全陌生,所以我不确定我是否从 NSData 转换为 CGImage 并错误地返回,或者是否需要调用某些方法再次解压缩.
I am totally new to using CGImage, so I am not sure if I am converting from NSData to CGImage and back incorrectly, or if I need to call some method to decompress again.
提前致谢,
会
推荐答案
我在做一些图像处理时遇到了你关于 SO 的问题.似乎没有其他人想出答案,所以这是我的理论.
I was doing some image manipulation and came across your question on SO. Seems like no one else came up with an answer, so here's my theory.
虽然理论上可以按照您描述的方式将 CGImageRef
转换回 NSData
,但数据本身是无效的,而不是真正的 JPEG 或 PNG,因为您发现它不可读.所以我不认为 NSData.length
是正确的.您实际上必须跳过许多步骤才能重新创建 CGImageRef
的 NSData
表示:
While it's theoretically possible to convert a CGImageRef
back to NSData
in the manner that you described, the data itself is invalid and not a real JPEG or PNG, as you discovered by it not being readable. So I don't think that the NSData.length
is correct. You have to actually jump through a number of steps to recreate an NSData
representation of a CGImageRef
:
// incoming image data
NSData *image;
// create the image ref
CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef) image);
CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);
// image metadata properties (EXIF, GPS, TIFF, etc)
NSDictionary *properties;
// create the new output data
CFMutableDataRef newImageData = CFDataCreateMutable(NULL, 0);
// my code assumes JPEG type since the input is from the iOS device camera
CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef) @"image/jpg", kUTTypeImage);
// create the destination
CGImageDestinationRef destination = CGImageDestinationCreateWithData(newImageData, type, 1, NULL);
// add the image to the destination
CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef) properties);
// finalize the write
CGImageDestinationFinalize(destination);
// memory cleanup
CGDataProviderRelease(imgDataProvider);
CGImageRelease(imageRef);
CFRelease(type);
CFRelease(destination);
NSData *newImage = (__bridge_transfer NSData *)newImageData;
通过这些步骤,newImage.length
应该与 image.length
相同.我没有测试,因为我实际上在输入和输出之间进行了裁剪,但是根据裁剪,大小大致是我的预期(输出大约是输入像素的一半,因此输出长度大约是大小的一半输入长度).
With these steps, the newImage.length
should be the same as image.length
. I haven't tested since I actually do cropping between the input and the output, but based on the crop, the size is roughly what I expected (the output is roughly half the pixels of the input and thus the output length roughly half the size of the input length).
这篇关于将 NSData 转换为 CGImage 然后再转换回 NSData 会使文件太大的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!