将NSData转换为CGImage,然后再转换回NSData会使文件变得太大。

5

我使用 AVFoundation 构建了一个相机。

AVCaptureStillImageOutput 完成其 captureStillImageAsynchronouslyFromConnection:completionHandler: 方法后,我像这样创建了一个 NSData 对象:

         NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];

一旦我获得了NSData对象,我想要旋转图片,而不需要转换为UIImage。我发现可以将其转换为CGImage来实现。

在我拥有图像数据后,我开始将其转换为CGImage的过程中,但我发现CGImageRefNSData对象大30倍。

这是我用于从NSData转换为CGImage的代码:

CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)(imageData));
CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);

如果我尝试使用NSLog输出图像的大小,当NSData只有1.5-2兆字节时,它会显示为30兆字节!

size_t imageSize = CGImageGetBytesPerRow(imageRef) * CGImageGetHeight(imageRef);

    NSLog(@"cgimage size = %zu",imageSize);

我认为当你从NSData转换为CGImage时,图像会被解压缩,然后如果我再将其转换回NSData,它可能会恢复到正确的文件大小。

imageData = (NSData *) CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(imageRef)));

上述的NSDataCGImageRef对象具有相同的length
如果我试图保存图片,那么这张图片是一个30MB大小的图片,无法打开。
我完全不了解如何使用CGImage,所以我不确定我是否将NSData转换为CGImage然后再次转换回去时出现了错误,或者我是否需要调用某些方法来重新解压缩。
提前致谢,
Will

你尝试过玩一下CGImageCreateWithJPEGDataProvider()的参数吗?例如不插值或使用不同的渲染意图?这些可能会影响压缩。例如,插值可能会增加文件大小,因为颜色混合会增加图像中所表示的颜色数量。 - Ryan Poolos
@RyanPoolos将shouldInterpolate设置为No似乎对文件大小没有影响。我想尽可能保留原始图像,但似乎其他因素正在影响我的代码...即使图像大小为30兆字节,我仍然应该能够保存和查看它,但目前我无法做到。 - Will
2个回答

8

我正在进行一些图像操作,偶然看到了您在SO上的问题。似乎没有其他人提出答案,所以这是我的理论。

虽然从理论上讲,可以按照您描述的方式将CGImageRef转换回NSData,但数据本身是无效的,并不是真正的JPEGPNG,正如您发现的那样,它无法读取。因此,我认为NSData.length不正确。您必须实际跳过多个步骤才能重新创建CGImageRefNSData表示:

// 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 相同。虽然我在输入和输出之间进行了裁剪,但是基于裁剪,大小大致符合我的预期(输出大致为输入像素的一半,因此输出长度大致为输入长度的一半)。

1

如果有人正在寻找“将CGImage转换为Data”的Swift版本,这里就是:

extension CGImage {
    var jpegData: Data? {
        guard let mutableData = CFDataCreateMutable(nil, 0),
              let destination = CGImageDestinationCreateWithData(mutableData, kUTTypeJPEG, 1, nil)
        else {
            return nil
        }
        CGImageDestinationAddImage(destination, self, nil)
        guard CGImageDestinationFinalize(destination) else { return nil }
        return mutableData as Data
    }

}


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