在iOS应用中,我有一个NSData
对象是一个JPEG文件,需要调整大小到给定的分辨率(2048x2048),并且需要将JPEG质量设置为75%。这些需要在保留文件中的EXIF数据的同时进行设置。照片不在相机胶卷中 - 它是通过网络从DSLR相机拉出来的,只是暂时与应用程序一起存储。如果图像通过UIImage进行,则EXIF数据将丢失。如何在不丢失EXIF的情况下执行调整大小和设置质量?或者有没有办法在转换之前去除EXIF数据,并在完成后将其添加回来?
答案 0 :(得分:4)
您可以尝试CGImageSourceCreateThumbnailAtIndex和CGImageSourceCopyPropertiesAtIndex来调整作为jpeg图像的NSData对象的大小而不会丢失EXIF。
我的灵感来自Problem setting exif data for an image
以下是我的代码用于相同的目的。
干杯
+ (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];
}
答案 1 :(得分:0)
Upvoted Greener Chen的答案,但这就是我在Swift 3中实现它的方式。 几条评论:
我认为你不能将kCGImageDestinationLossyCompressionQuality
之类的目标属性设置为CGImageSourceCreateThumbnailAtIndex()
CGImageSource
函数 - 但从未尝试过它。
请注意,通过将相关密钥(kCGImageDestinationLossyCompressionQuality
)添加到保留的源元数据集中作为CGImageDestinationAddImage
最初让我感到困惑的是,虽然我们正在为调整大小的目标图像提供完整的源图像元数据,CGImageDestinationAddImage()
足够聪明,可以忽略源图像的任何维度数据(W + H)并使用正确的,已调整大小的图像,尺寸自动替换它。所以PixelHeight
,PixelWidth
(“根”元数据)& 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
}
答案 2 :(得分:0)
我遇到了同样的问题,现在我可以使用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);
答案 3 :(得分:-2)
您可以使用ExifTool之类的实用程序来删除和恢复EXIF。这是一个跨平台的命令行实用程序。以下是执行所需操作的相应命令。
删除EXIF:
exiftool -exif = image.jpg
在编辑图像后再次恢复EXIF:
exiftool -tagsfromfile image.jpg_original -exif image.jpg
在这个例子中,我利用了ExifTool自动生成的“_original”备份。