一、图片压缩简述
首先,我们必须明确图片的压缩其实是两个概念:
- “压” 是指文件体积变小,但是像素数不变,长宽尺寸不变,那么质量可能下降。
- “缩” 是指文件的尺寸变小,也就是像素数减少,而长宽尺寸变小,文件体积同样会减小。
二、图片压缩的实现
2.1 “压”处理
对于“压”的功能,我们一般是使用系统提供的UIImageJPEGRepresentation或UIImagePNGRepresentation方法实现,如:
// return image as PNG. May return nil if image has no CGImageRef or invalid bitmap format UIKIT_EXTERN NSData *UIImagePNGRepresentation(UIImage *image); // return image as JPEG. May return nil if image has no CGImageRef or invalid bitmap format. compression is 0(most)..1(least) UIKIT_EXTERN NSData *UIImageJPEGRepresentation(UIImage *image, CGFloat compressionQuality); //UIImageJPEGRepresentation需要传两个参数, //第一个参数是图片对象 //第二个参数是压的系数,其值范围为0~1 NSData *imgData=UIImageJPEGRepresentation(image, 0.5); //UIImagePNGRepresentation只需要传一个参数,就是图片对象 NSData *imgData = UIImagePNGRepresentation(image);
UIImagePNGRepresentation要比UIImageJPEGRepresentation(UIImage* image, 1.0)返回的图片数据量大很多。同样的一张照片, 使用UIImagePNGRepresentation(image)返回的数据量大小为199K,而UIImageJPEGRepresentation(image, 1.0)返回的数据量大小只为140K,比前者少了59K。
如果对图片的清晰度要求不是极高,建议使用UIImageJPEGRepresentation,可以大幅度降低图片数据量.其中UIImageJPEGRepresentation(UIImage *image, CGFloat compressionQuality)提供了一个压缩比率的参数compressionQuality,但是实际体验确实compressionQuality并不能够按照设定好的数值,比例压缩。比如一张2.9M的图片(jpg格式),通过UIImageJPEGRepresentation方法设置不同压缩比进行压缩后的大小如下:
2019-03-13 13:54:33.546342+0800 CJMobile[52591:15764262] compression = 1.000000 image length = 7076.682617 kB 2019-03-13 13:54:33.658606+0800 CJMobile[52591:15764262] compression = 0.500000 image length = 1490.095703 kB 2019-03-13 13:54:33.748077+0800 CJMobile[52591:15764262] compression = 0.250000 image length = 671.213867 kB 2019-03-13 13:54:33.834126+0800 CJMobile[52591:15764262] compression = 0.125000 image length = 550.979492 kB 2019-03-13 13:54:33.918830+0800 CJMobile[52591:15764262] compression = 0.062500 image length = 532.168945 kB 2019-03-13 13:54:34.004086+0800 CJMobile[52591:15764262] compression = 0.031250 image length = 532.107422 kB 2019-03-13 13:54:34.089819+0800 CJMobile[52591:15764262] compression = 0.015625 image length = 532.107422 kB
通过上面的结果我们可以看到,compressionQuality压缩系数跟最后文件的大小并没有明显的关系,不同的图片呈现不同结果,而且最后压缩比减小但是得到的图片大小没有变化。本人对图片存储格式不是很了解,所以对出现这样的情况不是很了解,如果有对此比较了解的同学烦请赐教。但是图片颜色细节越单一,图片可压缩的比率会越高。
UIImagePNGRepresentation虽然可以让我们控制压缩质量比例,但是我们看到这个压缩比compressionQuality实际上很难确定一张图片是否能压缩到误差范围内,无法实现精确压缩。
2.2 “缩”处理
UIImagePNGRepresentation虽然可以让我们控制压缩质量比例,但是我们看到这个压缩比compressionQuality实际上很难确定一张图片是否能压缩到误差范围内,无法实现精确压缩。所以我们对图片只“压”而不缩,有时候是达不到我们的需求的。因此,必要的时候,我们需要适当地对图片“缩”一“缩“尺寸,就可以满足我们的需求。
通过 [sourceImage drawInRect:CGRectMake(0, 0, targetWidth, targetHeight)] 可以进行图片“缩”的功能。示例如下:
- (UIImage*)compressImage:(UIImage*)sourceImage toTargetWidth:(CGFloat)targetWidth { //获取原图片的大小尺寸 CGSize imageSize = sourceImage.size; CGFloat width = imageSize.width; CGFloat height = imageSize.height; //根据目标图片的宽度计算目标图片的高度 CGFloat targetHeight = (targetWidth / width) * height; //开启图片上下文 UIGraphicsBeginImageContext(CGSizeMake(targetWidth, targetHeight)); //绘制图片 [sourceImage drawInRect:CGRectMake(0,0, targetWidth, targetHeight)]; //从上下文中获取绘制好的图片 UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext(); //关闭图片上下文 UIGraphicsEndImageContext(); return newImage; }
通过“缩”处理,我们可以将图片压缩到任何我们制定的大小尺寸内,但是这种处理,我们改变了原先图片的尺寸大小,无法保证图片的质量。
三、图片压缩到指定大小以内实现
当我们需要对图片的大小进行限制时,我们首先应该优先采取“压”处理,如果“压”处理达不到要求,那么我们在“压”处理的结果上继续进行“缩”处理,直到图片的大小达到我们的要求为止。
/*! * @brief 使图片压缩后刚好小于指定大小 * * @param image 当前要压缩的图 maxLength 压缩后的大小 * * @return 图片对象 */ //图片质量压缩到某一范围内,如果后面用到多,可以抽成分类或者工具类,这里压缩递减比二分的运行时间长,二分可以限制下限。 - (UIImage *)compressImageSize:(UIImage *)image toByte:(NSUInteger)maxLength{ //首先判断原图大小是否在要求内,如果满足要求则不进行压缩,over CGFloat compression = 1; NSData *data = UIImageJPEGRepresentation(image, compression); if (data.length < maxLength) return image; //原图大小超过范围,先进行“压处理”,这里 压缩比 采用二分法进行处理,6次二分后的最小压缩比是0.015625,已经够小了 CGFloat max = 1; CGFloat min = 0; for (int i = 0; i < 6; ++i) { compression = (max + min) / 2; data = UIImageJPEGRepresentation(image, compression); if (data.length < maxLength * 0.9) { min = compression; } else if (data.length > maxLength) { max = compression; } else { break; } } //判断“压处理”的结果是否符合要求,符合要求就over UIImage *resultImage = [UIImage imageWithData:data]; if (data.length < maxLength) return resultImage; //缩处理,直接用大小的比例作为缩处理的比例进行处理,因为有取整处理,所以一般是需要两次处理 NSUInteger lastDataLength = 0; while (data.length > maxLength && data.length != lastDataLength) { lastDataLength = data.length; //获取处理后的尺寸 CGFloat ratio = (CGFloat)maxLength / data.length; CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)), (NSUInteger)(resultImage.size.height * sqrtf(ratio))); //通过图片上下文进行处理图片 UIGraphicsBeginImageContext(size); [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)]; resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); //获取处理后图片的大小 data = UIImageJPEGRepresentation(resultImage, compression); } return resultImage; }