Calling imageWithData:UIImageJPEGRepresentation() multiple times only compresses image the first time

半世苍凉 提交于 2019-11-30 16:21:49
Rob

A couple of thoughts:

  1. The UIImageJPEGRepresentation function does not return the "original" image. For example, if you employ a compressionQuality of 1.0, it does not, technically, return the "original" image, but rather it returns a JPEG rendition of the image with compressionQuality at its maximum value. This can actually yield an object that is larger than the original asset (at least if the original image is a JPEG). You're also discarding all of the metadata (information about where the image was taken, the camera settings, etc.) in the process.

  2. If you want the original asset, you should use PHImageManager:

    NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL];
    
    PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[url] options:nil];
    PHAsset *asset = [result firstObject];
    
    PHImageManager *manager = [PHImageManager defaultManager];
    [manager requestImageDataForAsset:asset options:nil resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
        NSString *filename = [(NSURL *)info[@"PHImageFileURLKey"] lastPathComponent];
        // do what you want with the `imageData`
    }];
    

    In iOS versions prior to 8, you'd have to use assetForURL of the ALAssetsLibrary class:

    NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL];
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    [library assetForURL:url resultBlock:^(ALAsset *asset) {
        ALAssetRepresentation *representation = [asset defaultRepresentation];
    
        NSLog(@"size of original asset %llu", [representation size]);
    
        // I generally would write directly to a `NSOutputStream`, but if you want it in a
        // NSData, it would be something like:
    
        NSMutableData *data = [NSMutableData data];
    
        // now loop, reading data into buffer and writing that to our data strea
        NSError *error;
        long long bufferOffset = 0ll;
        NSInteger bufferSize = 10000;
        long long bytesRemaining = [representation size];
        uint8_t buffer[bufferSize];
        NSUInteger bytesRead;
        while (bytesRemaining > 0) {
            bytesRead = [representation getBytes:buffer fromOffset:bufferOffset length:bufferSize error:&error];
            if (bytesRead == 0) {
                NSLog(@"error reading asset representation: %@", error);
                return;
            }
            bytesRemaining -= bytesRead;
            bufferOffset   += bytesRead;
            [data appendBytes:buffer length:bytesRead];
        }
    
        // ok, successfully read original asset; 
        // do whatever you want with it here
    
    } failureBlock:^(NSError *error) {
        NSLog(@"error=%@", error);
    }];
    

    Please note that this assetForURL runs asynchronously.

  3. If you want a NSData with compression, you can use UIImageJPEGRepresentation with a compressionQuality less than 1.0. Your code actually does this with a compressionQuality of 0.0, which should offer maximum compression. But you don't save that NSData, but rather use it to create a UIImage and you then get a new UIImageJPEGRepresentation with a compressionQuality of 1.0, thus losing much of the compression you originally achieved.

    Consider the following code:

    // a UIImage of the original asset (discarding meta data)
    
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    
    // this may well be larger than the original asset
    
    NSData *jpgDataHighestCompressionQuality = UIImageJPEGRepresentation(image, 1.0);
    [jpgDataHighestCompressionQuality writeToFile:[docsPath stringByAppendingPathComponent:@"imageDataFromJpeg.jpg"] atomically:YES];
    NSLog(@"compressionQuality = 1.0; length = %u", [jpgDataHighestCompressionQuality length]);
    
    // this will be smaller, but with some loss of data
    
    NSData *jpgDataLowestCompressionQuality = UIImageJPEGRepresentation(image, 0.0);
    NSLog(@"compressionQuality = 0.0; length = %u", [jpgDataLowestCompressionQuality length]);
    UIImage *image2 = [UIImage imageWithData:jpgDataLowestCompressionQuality];
    
    // ironically, this will be larger than jpgDataLowestCompressionQuality
    
    NSData *newImageSize = UIImageJPEGRepresentation(image2, 1.0);
    NSLog(@"new size %u", [newImageSize length]);
    
  4. In addition to the JPEG compression quality outlined the prior point, you could also just resize the image. You can also marry this with the JPEG compressionQuality, too.

You can not compress the image again and again. If so everything can be compressed again and again. Then how small do you think it will be?

One way to make your image smaller is to change it's size. For example change 640X960 to 320X480. But you will lose quality.

I is the first implementation of UIImageJPEGRepresentation (image, 0.75), and then change the size. Maybe image's width and heigh two-thirds or half.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!