1

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

4

4 回答 4

5

您可以尝试使用 CGImageSourceCreateThumbnailAtIndex 和 CGI​​mageSourceCopyPropertiesAtIndex 来调整作为 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];
}
于 2014-04-16T10:19:19.020 回答
0

赞成 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
    

    }

于 2017-08-01T13:42:59.367 回答
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);
于 2017-11-27T23:47:39.403 回答
-3

您可以使用ExifTool之类的实用程序来剥离和恢复 EXIF。这是一个跨平台的命令行实用程序。以下是执行所需操作的适当命令。

要删除 EXIF:

exiftool -exif= image.jpg

编辑图像后再次恢复 EXIF:

exiftool -tagsfromfile image.jpg_original -exif image.jpg

在此示例中,我利用了 ExifTool 自动生成的“_original”备份。

于 2013-08-12T17:41:54.657 回答