调整JPEG图像的大小和设置质量,同时在iOS中保留EXIF

时间:2013-08-09 17:03:34

标签: ios objective-c nsdata exif

在iOS应用中,我有一个NSData对象是一个JPEG文件,需要调整大小到给定的分辨率(2048x2048),并且需要将JPEG质量设置为75%。这些需要在保留文件中的EXIF数据的同时进行设置。照片不在相机胶卷中 - 它是通过网络从DSLR相机拉出来的,只是暂时与应用程序一起存储。如果图像通过UIImage进行,则EXIF数据将丢失。如何在不丢失EXIF的情况下执行调整大小和设置质量?或者有没有办法在转换之前去除EXIF数据,并在完成后将其添加回来?

4 个答案:

答案 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中实现它的方式。 几条评论:

  • 我的函数接受一个jpeg缓冲区并返回一个jpeg缓冲区。它还会检查是否需要调整大小(检查源的最大维度),如果此处没有任何操作,则只返回输入缓冲区。您可能希望适应您的用例
  • 我认为你不能将kCGImageDestinationLossyCompressionQuality之类的目标属性设置为CGImageSourceCreateThumbnailAtIndex() CGImageSource函数 - 但从未尝试过它。

  • 请注意,通过将相关密钥(kCGImageDestinationLossyCompressionQuality)添加到保留的源元数据集中作为CGImageDestinationAddImage

    的选项

    ,将压缩添加到目标图像
  • 最初让我感到困惑的是,虽然我们正在为调整大小的目标图像提供完整的源图像元数据,CGImageDestinationAddImage()足够聪明,可以忽略源图像的任何维度数据(W + H)并使用正确的,已调整大小的图像,尺寸自动替换它。所以PixelHeightPixelWidth(“根”元数据)& 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”备份。