如何通过数据大小缩减IOS中的UIImage

我期待在iOS缩减UIImage

我已经看到下面的其他问题,以及如何按尺寸缩小图像的方法。 调整图像大小Objective-C
如何在iPhone中通过objective-c以编程方式调整图像大小
调整UIImage的最简单的方法是什么?

这些问题都是基于将图像重新调整到特定的大小。 在我的情况下,我正在寻找基于最大尺寸重新缩放/缩小图像。

作为一个例子,我想设置一个最大的NSData大小为500 KB。 我知道我可以像这样获取图像的大小://检查返回的图像的大小

 NSData *imageData = UIImageJPEGRepresentation(image, 0.5); // Log out the image size NSLog(@"%lu KB",(imageData.length/1024)); 

我想要做的是这里的某种forms的循环。 如果尺寸大于我设定的最大尺寸,我想稍微缩小图像,然后检查尺寸。 如果尺寸仍然过小,则再次检查,直到它低于最大设定尺寸。

我不确定这是什么最好的方法。 理想情况下,我不想一直缩小图像到一个特定的大小,但只是每次稍微缩小图像。 这样,我可以拥有图像本身的最大(大小w / h)和最大大小(字节)。 如果我一次只稍微缩小,那么完成这个的最好方法是什么?

编辑为了确认,我正在寻找重新调整实际图像的大小,但重新调整图像的大小,使其小于最大的NSData长度。 例如:
– 检查NSData长度
如果超过最大值,我想将UIImage传递给一个方法
然后通过这个方法循环每次稍微调整实际的图像大小
– 直到它在最大的NSData长度下,然后返回图像?

现在,你有一个例程,说:

 // Check if the image size is too large if ((imageData.length/1024) >= 1024) { while ((imageData.length/1024) >= 1024) { NSLog(@"While start - The imagedata size is currently: %f KB",roundf((imageData.length/1024))); // While the imageData is too large scale down the image // Get the current image size CGSize currentSize = CGSizeMake(image.size.width, image.size.height); // Resize the image image = [image resizedImage:CGSizeMake(roundf(((currentSize.width/100)*80)), roundf(((currentSize.height/100)*80))) interpolationQuality:kMESImageQuality]; // Pass the NSData out again imageData = UIImageJPEGRepresentation(image, kMESImageQuality); } } 

我不会build议recursion调整图像大小。 每次resize时,都会失去一些质量(通常performance为图像“软化”,失去细节和累积效果)。 你总是想回到原来的形象,resize。 (作为未成年人, if陈述也是多余的。)

我可能会build议如下:

 NSData *imageData = UIImageJPEGRepresentation(image, kMESImageQuality); double factor = 1.0; double adjustment = 1.0 / sqrt(2.0); // or use 0.8 or whatever you want CGSize size = image.size; CGSize currentSize = size; UIImage *currentImage = image; while (imageData.length >= (1024 * 1024)) { factor *= adjustment; currentSize = CGSizeMake(roundf(size.width * factor), roundf(size.height * factor)); currentImage = [image resizedImage:currentSize interpolationQuality:kMESImageQuality]; imageData = UIImageJPEGRepresentation(currentImage, kMESImageQuality); } 

请注意,我并不是触摸image ,原始图像,而是每次都通过从原始图像resize来指定currentImage ,每次都会减小比例。

顺便说一句,如果你想知道我的神秘的1.0 / sqrt(2.0) ,我试图在80%的迭代因子和我希望通过2的倍数来resize的愿望之间作出妥协(因为降低了更多清晰度由2的幂完成)。 但是使用你想要的任何adjustment因素。

最后,如果你正在做这个巨大的图像,你可以考虑使用@autoreleasepool块。 您将需要在“乐器configuration”中对您的应用进行configuration,并查看高水位的位置,因为在没有autorelease池的情况下,这可能会构成相当积极的内存使用。

除了最大尺寸外,还需要select最小尺寸以及决定性能。 例如,你可以检查UIImageJPEGRepresentation(image, 1.0)的大小。 如果太大,你是在0.95还是0.1

一种可能的方法是获得UIImageJPEGRepresentation(image, 1.0)的大小,并看看它是多大的百分比。 例如,说它是600kB。 然后你应该计算大约0.83 500.0 / 600 。 那么做UIImageJPEGRepresentation(image, 0.83) 。 这不会给完全500kB,但可能是足够接近。

另一种方法是从UIImageJPEGRepresentation(image, 1.0) 。 这是太大,然后做UIImageJPEGRepresentation(image, 0.5)如果太大,然后去0.25但如果太小去0.75 。 不断分解差异,直到达到您所需大小的可接受范围内。

这是我的方法:

 // Check if the image size is too large if ((imageData.length/1024) >= 1024) { while ((imageData.length/1024) >= 1024) { NSLog(@"While start - The imagedata size is currently: %f KB",roundf((imageData.length/1024))); // While the imageData is too large scale down the image // Get the current image size CGSize currentSize = CGSizeMake(image.size.width, image.size.height); // Resize the image image = [image resizedImage:CGSizeMake(roundf(((currentSize.width/100)*80)), roundf(((currentSize.height/100)*80))) interpolationQuality:kMESImageQuality]; // Pass the NSData out again imageData = UIImageJPEGRepresentation(image, kMESImageQuality); } } 

调整图像大小的方法如下:

 // Returns a rescaled copy of the image, taking into account its orientation // The image will be scaled disproportionately if necessary to fit the bounds specified by the parameter - (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality { BOOL drawTransposed; switch (self.imageOrientation) { case UIImageOrientationLeft: case UIImageOrientationLeftMirrored: case UIImageOrientationRight: case UIImageOrientationRightMirrored: drawTransposed = YES; break; default: drawTransposed = NO; } return [self resizedImage:newSize transform:[self transformForOrientation:newSize] drawTransposed:drawTransposed interpolationQuality:quality]; } 

其次是:

 // Returns a copy of the image that has been transformed using the given affine transform and scaled to the new size // The new image's orientation will be UIImageOrientationUp, regardless of the current image's orientation // If the new size is not integral, it will be rounded up - (UIImage *)resizedImage:(CGSize)newSize transform:(CGAffineTransform)transform drawTransposed:(BOOL)transpose interpolationQuality:(CGInterpolationQuality)quality { CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width); CGImageRef imageRef = self.CGImage; // Build a context that's the same dimensions as the new size CGContextRef bitmap = CGBitmapContextCreate(NULL, newRect.size.width, newRect.size.height, CGImageGetBitsPerComponent(imageRef), 0, CGImageGetColorSpace(imageRef), CGImageGetBitmapInfo(imageRef)); // Rotate and/or flip the image if required by its orientation CGContextConcatCTM(bitmap, transform); // Set the quality level to use when rescaling CGContextSetInterpolationQuality(bitmap, quality); // Draw into the context; this scales the image CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef); // Get the resized image from the context and a UIImage CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap); UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; // Clean up CGContextRelease(bitmap); CGImageRelease(newImageRef); return newImage; } 

//其他方法供参考

 // Returns an affine transform that takes into account the image orientation when drawing a scaled image - (CGAffineTransform)transformForOrientation:(CGSize)newSize { CGAffineTransform transform = CGAffineTransformIdentity; switch (self.imageOrientation) { case UIImageOrientationDown: // EXIF = 3 case UIImageOrientationDownMirrored: // EXIF = 4 transform = CGAffineTransformTranslate(transform, newSize.width, newSize.height); transform = CGAffineTransformRotate(transform, M_PI); break; case UIImageOrientationLeft: // EXIF = 6 case UIImageOrientationLeftMirrored: // EXIF = 5 transform = CGAffineTransformTranslate(transform, newSize.width, 0); transform = CGAffineTransformRotate(transform, M_PI_2); break; case UIImageOrientationRight: // EXIF = 8 case UIImageOrientationRightMirrored: // EXIF = 7 transform = CGAffineTransformTranslate(transform, 0, newSize.height); transform = CGAffineTransformRotate(transform, -M_PI_2); break; default: break; } switch (self.imageOrientation) { case UIImageOrientationUpMirrored: // EXIF = 2 case UIImageOrientationDownMirrored: // EXIF = 4 transform = CGAffineTransformTranslate(transform, newSize.width, 0); transform = CGAffineTransformScale(transform, -1, 1); break; case UIImageOrientationLeftMirrored: // EXIF = 5 case UIImageOrientationRightMirrored: // EXIF = 7 transform = CGAffineTransformTranslate(transform, newSize.height, 0); transform = CGAffineTransformScale(transform, -1, 1); break; default: break; } return transform; } 
 -(NSData*)testData { UIImage *imageToUpload=[UIImage imageNamed:@"images/lifestyle2.jpg"]; NSData *imgData=UIImageJPEGRepresentation(imageToUpload,1.0); float compressionRate=10; while (imgData.length>1024) { if (compressionRate>0.5) { compressionRate=compressionRate-0.5; imgData=UIImageJPEGRepresentation(imageToUpload,compressionRate/10); } else { return imgData; } } return imgData; } 

它保持图像质量不低于1MB。

与之呼应,

 NSData *compressedImageData=[self testData]; NSLog(@"%lu KB",compressedImageData.length); 

在您修改的问题中,您澄清了您的目标是在上传图片时说明文件大小限制。 在这种情况下,使用JPEG压缩选项可以达到rmaddy所build议的效果。

有趣的问题是,你有两个variables,JPEG压缩和图像尺寸(也有其他的,但我会保持简单)。 你想如何优先考虑一个呢? 例如,我认为保持全分辨率,荒谬的压缩图像(例如0.1质量因子)是不合理的。 保持一个微小的分辨率,不压缩的图像也没有意义。 就个人而言,我会按照rmaddy的build议反复调整质量,但是要设置一些合理的层次(例如,JPEG质量不低于0.70)。 此时,我可能会考虑更改图像尺寸(并且也会相当快地更改文件大小),并更改尺寸直到生成的NSData尺寸合适。

无论如何,在我原来的答案,我专注于应用程序内存消耗(而不是文件大小)。 为了后代的缘故,请看下面的答案:


如果您试图控制将图像加载到要在UIKit对象中使用的UIImage对象中时使用了多less内存,那么使用JPEG压缩function将无济于事,因为一旦加载它们后,图像的内部表示到UIKit对象是未压缩的。 因此,在这种情况下,JPEG压缩选项并没有取得很大的成就(牺牲图像质量除外)。

为了说明这个想法,我有一个1920 x 1080的图像。我使用PNG格式(文件为629kb),压缩的JPEG格式(217kb)和最小压缩的JPEG格式(1.1mb)。 但是,当我将这三个不同的图像加载到UIImageView对象(即使它们有一个非常小的frame )时,Instrument的“Allocations”工具显示它们每个占用7.91mb:

分配

这是因为当您将图像加载到图像视图中时,这三个图像的内部未压缩表示是每像素四个字节(一个字节红色,一个绿色,一个蓝色,一个字母)。 因此,我的1920 x 1080图像占用了1920 x 1080 x 4 = 8,249,400 = 7.91mb。

所以,如果你不想在内存中占用超过500kb的内存时,这意味着你要调整它们的大小,使得宽度乘以高度的乘积将会是128,000或更less(即如果是正方形,则小于358×358像素)。

但是,如果您在上载图片或持续存储容量时担心networking带宽问题,那么请继续使用rmaddy出色的答案所提供的JPEG压缩值。 但是,如果在图像加载到UIKit对象时试图解决内存消耗问题,那么请不要关注压缩,而是专注于调整图像大小。