在 iOS 应用程序中,我有NSData
一个 JPEG 文件对象,需要将其大小调整为给定的分辨率 (2048x2048),并且需要将 JPEG 质量设置为 75%。这些需要在文件中保留 EXIF 数据的同时进行设置。照片不在相机胶卷中——它是从数码单反相机通过网络提取的,只是临时存储在应用程序中。如果图像通过 UIImage 进行传输,则 EXIF 数据会丢失。如何在不丢失 EXIF 的情况下执行调整大小和设置质量?或者有没有办法在转换之前剥离 EXIF 数据,并在完成后将其添加回来?
4 回答
您可以尝试使用 CGImageSourceCreateThumbnailAtIndex 和 CGImageSourceCopyPropertiesAtIndex 来调整作为 jpeg 图像的 NSData 对象的大小而不会丢失 EXIF。
我的灵感来自为图像设置 exif 数据的问题
以下是我用于相同目的的代码。
干杯
+ (NSData *)JPEGRepresentationSavedMetadataWithImage:(NSData *)imageData compressionQuality:(CGFloat)compressionQuality maxSize:(CGFloat)maxSize
{
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
CFDictionaryRef options = (__bridge CFDictionaryRef)@{(id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue,
(id)kCGImageSourceCreateThumbnailFromImageIfAbsent: (id)kCFBooleanTrue,
(id)kCGImageSourceThumbnailMaxPixelSize: [NSNumber numberWithDouble: maxSize], // The maximum width and height in pixels of a thumbnail
(id)kCGImageDestinationLossyCompressionQuality: [NSNumber numberWithDouble:compressionQuality]};
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(source, 0, options); // Create scaled image
CFStringRef UTI = kUTTypeJPEG;
NSMutableData *destData = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)destData, UTI, 1, NULL);
if (!destination) {
NSLog(@"Failed to create image destination");
}
CGImageDestinationAddImage(destination, thumbnail, CGImageSourceCopyPropertiesAtIndex(source, 0, NULL)); // copy all metadata in source to destination
if (!CGImageDestinationFinalize(destination)) {
NSLog(@"Failed to create data from image destination");
}
CFRelease(destination);
CFRelease(source);
CFRelease(thumbnail);
return [destData copy];
}
赞成 Greener Chen 的回答,但这就是我在 Swift 3 中实现它的方式。很少有评论:
- 我的函数需要一个 jpeg 缓冲区并返回一个 jpeg 缓冲区。它还检查是否需要调整大小(检查源的最大尺寸),如果这里无事可做,则简单地返回输入缓冲区。您可能希望适应您的用例
我不认为您可以设置像函数一样的目标属性-
kCGImageDestinationLossyCompressionQuality
但从未尝试过。CGImageSourceCreateThumbnailAtIndex()
CGImageSource
请注意,压缩是通过将相关键 (
kCGImageDestinationLossyCompressionQuality
) 添加到保留的源元数据集作为选项来添加到目标图像的CGImageDestinationAddImage
最初让我感到困惑的是,尽管我们为调整大小的目标图像提供了完整的源图像元数据,但
CGImageDestinationAddImage()
它足够聪明,可以忽略源图像的任何尺寸数据 (W+H) 并自动将其替换为正确的、调整大小的图像,方面。所以PixelHeight
,PixelWidth
("root" metadata) &PixelXDimension
&PixelYDimension
(EXIF) 不会从源中继承,而是正确设置为调整大小的图像尺寸。class func resizeImage(imageData: Data, maxResolution: Int, compression: CGFloat) -> Data? { // create image source from jpeg data if let myImageSource = CGImageSourceCreateWithData(imageData as CFData, nil) { // get source properties so we retain metadata (EXIF) for the downsized image if var metaData = CGImageSourceCopyPropertiesAtIndex(myImageSource,0, nil) as? [String:Any], let width = metaData[kCGImagePropertyPixelWidth as String] as? Int, let height = metaData[kCGImagePropertyPixelHeight as String] as? Int { let srcMaxResolution = max(width, height) // if max resolution is exceeded, then scale image to new resolution if srcMaxResolution >= maxResolution { let scaleOptions = [ kCGImageSourceThumbnailMaxPixelSize as String : maxResolution, kCGImageSourceCreateThumbnailFromImageAlways as String : true] as [String : Any] if let scaledImage = CGImageSourceCreateThumbnailAtIndex(myImageSource, 0, scaleOptions as CFDictionary) { // add compression ratio to desitnation options metaData[kCGImageDestinationLossyCompressionQuality as String] = compression //create new jpeg let newImageData = NSMutableData() if let cgImageDestination = CGImageDestinationCreateWithData(newImageData, "public.jpeg" as CFString, 1, nil) { CGImageDestinationAddImage(cgImageDestination, scaledImage, metaData as CFDictionary) CGImageDestinationFinalize(cgImageDestination) return newImageData as Data } } } } } return nil
}
我遇到了同样的问题,现在我可以上传带有 EXIF 数据的文件,也可以根据需要压缩照片,这为我解决了问题:
// Get your image.
NSURL *url = @"http://somewebsite.com/path/to/some/image.jpg";
UIImage *loImgPhoto = [NSData dataWithContentsOfURL:url];
// Get your metadata (includes the EXIF data).
CGImageSourceRef loImageOriginalSource = CGImageSourceCreateWithData(( CFDataRef) loDataFotoOriginal, NULL);
NSDictionary *loDicMetadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(loImageOriginalSource, 0, NULL);
// Set your compression quality (0.0 to 1.0).
NSMutableDictionary *loDicMutableMetadata = [loDicMetadata mutableCopy];
[loDicMutableMetadata setObject:@(lfCompressionQualityValue) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality];
// Create an image destination.
NSMutableData *loNewImageDataWithExif = [NSMutableData data];
CGImageDestinationRef loImgDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)loNewImageDataWithExif, CGImageSourceGetType(loImageOriginalSource), 1, NULL);
// Add your image to the destination.
CGImageDestinationAddImage(loImgDestination, loImgPhoto.CGImage, (__bridge CFDictionaryRef) loDicMutableMetadata);
// Finalize the destination.
if (CGImageDestinationFinalize(loImgDestination))
{
NSLog(@"Successful image creation.");
// process the image rendering, adjustment data creation and finalize the asset edit.
//Upload photo with EXIF metadata
[self myUploadMethod:loNewImageDataWithExif];
}
else
{
NSLog(@"Error -> failed to finalize the image.");
}
CFRelease(loImageOriginalSource);
CFRelease(loImgDestination);
您可以使用ExifTool之类的实用程序来剥离和恢复 EXIF。这是一个跨平台的命令行实用程序。以下是执行所需操作的适当命令。
要删除 EXIF:
exiftool -exif= image.jpg
编辑图像后再次恢复 EXIF:
exiftool -tagsfromfile image.jpg_original -exif image.jpg
在此示例中,我利用了 ExifTool 自动生成的“_original”备份。