问题
In order to prevent lagging in my app, I'm trying to compress images larger than 1 MB (mostly for pics taken from iphone's normal camera.
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSData *imageSize = UIImageJPEGRepresentation(image, 1);
NSLog(@"original size %u", [imageSize length]);
UIImage *image2 = [UIImage imageWithData:UIImageJPEGRepresentation(image, 0)];
NSData *newImageSize = UIImageJPEGRepresentation(image2, 1);
NSLog(@"new size %u", [newImageSize length]);
UIImage *image3 = [UIImage imageWithData:UIImageJPEGRepresentation(image2, 0)];
NSData *newImageSize2 = UIImageJPEGRepresentation(image3, 1);
NSLog(@"new size %u", [newImageSize2 length]);
picView = [[UIImageView alloc] initWithImage:image3] ;
However, the NSLog I get outputs something along the lines of
original size 3649058
new size 1835251
new size 1834884
The difference between the 1st and 2nd compression is almost negligible. My goal is to get the image size below 1 MB. Did I overlook something/is there an alternative approach to achieve this?
EDIT: I want to avoid scaling the image's height and width, if possible.
回答1:
A couple of thoughts:
The
UIImageJPEGRepresentation
function does not return the "original" image. For example, if you employ acompressionQuality
of1.0
, it does not, technically, return the "original" image, but rather it returns a JPEG rendition of the image withcompressionQuality
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.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 theALAssetsLibrary
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.If you want a
NSData
with compression, you can useUIImageJPEGRepresentation
with acompressionQuality
less than1.0
. Your code actually does this with acompressionQuality
of0.0
, which should offer maximum compression. But you don't save thatNSData
, but rather use it to create aUIImage
and you then get a newUIImageJPEGRepresentation
with acompressionQuality
of1.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]);
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.
回答2:
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.
回答3:
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.
来源:https://stackoverflow.com/questions/17005456/calling-imagewithdatauiimagejpegrepresentation-multiple-times-only-compresses