多次调用imageWithData:UIImageJPEGRepresentation()仅在第一次压缩图像 [英] Calling imageWithData:UIImageJPEGRepresentation() multiple times only compresses image the first time
问题描述
为了防止应用滞后,我尝试压缩大于1 MB的图像(主要用于从iphone的普通相机拍摄的照片.
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] ;
但是,我得到的NSLog输出类似于
However, the NSLog I get outputs something along the lines of
original size 3649058
new size 1835251
new size 1834884
第一和第二压缩之间的差异几乎可以忽略不计.我的目标是使图像大小低于1 MB.我是否忽略了某些事情/是否有替代方法来实现这一目标?
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?
编辑:如果可能,我想避免缩放图像的高度和宽度.
I want to avoid scaling the image's height and width, if possible.
推荐答案
一些想法:
-
UIImageJPEGRepresentation
函数不返回原始"图像.例如,如果使用compressionQuality
为1.0
,则从技术上讲,它不会返回原始"图像,而是会返回最大值为compressionQuality
的图像的JPEG格式.实际上,这可以产生大于原始资源的对象(至少在原始图像为JPEG的情况下).您还将在此过程中丢弃所有元数据(有关拍摄图像的位置,相机设置等的信息).
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.
如果要使用原始资产,则应使用PHImageManager
:
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`
}];
在8之前的iOS版本中,您必须使用ALAssetsLibrary
类的assetForURL
:
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);
}];
请注意,此assetForURL
是异步运行的.
Please note that this assetForURL
runs asynchronously.
如果要使用压缩的NSData
,则可以将UIImageJPEGRepresentation
与小于1.0
的compressionQuality
一起使用.您的代码实际上使用0.0
的compressionQuality
执行此操作,这应该提供最大的压缩率.但是,您不必保存该NSData
,而是使用它创建一个UIImage
,然后获得一个新的UIImageJPEGRepresentation
,其compressionQuality
为1.0
,从而失去了最初获得的大部分压缩率.
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.
考虑以下代码:
// 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]);
除了前面提到的JPEG压缩质量外,您还可以调整图像大小.您也可以将其与JPEG compressionQuality
结合使用.
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.
这篇关于多次调用imageWithData:UIImageJPEGRepresentation()仅在第一次压缩图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!