背景:
最近遇到一个图片压缩的问题,项目需求压缩图片500k以内上传服务器,还要求图片要清晰一点。事实证明500k的图片肉眼识别已经是很清晰,那就没办法了,硬着头皮上了。下面是我的一些心得体会,以及考虑到的一些方向,拿出来以供大家参考。
思路:
本来以为很简单的问题,自己随意写了一个UIImageJPEGRepresentation的方法进行一个循环压缩不就搞定了?后来事实证明这个想法是错的,有太多东西不是你想当然的。
问题:
1.为什么不提UIImagePNGRepresentation(<#UIImage * _Nonnull image#>)?
回复:据说这个读取图片的大小会比较大,因为是png格式,读取的内容会有多图层的的问题导致读取的会显示比较大,而且比较耗时间。网上有人做过测试:同样是读取摄像头拍摄的同样景色的照片,UIImagePNGRepresentation() 返回的数据量大小为199K,而 UIImageJPEGRepresentation(UIImage* image, 1.0) 返回的数据量大小只为 140KB,比前者少了50多KB。如果对图片的清晰度要求不高,还可以通过设置 UIImageJPEGRepresentation 函数的第二个参数,来降低图片数据量。
如果还有什么问题可以继续百度,这里不进行过多赘述。
2.首先第一个参数是我们都知道的图片image,但是第二个参数scale,一个0~1的浮点型比率,你以为0就是没有,压缩到0b大小,1.0就是原图大小?
答案是:错!首先你的图片的大小是根据(图片的宽*图片的高*每一个色彩的深度,这个和手机的硬件有关,一般是4)。其次,第二个参数苹果官方并没有明确说明这个参数的具体意义。对于大图片来说,即使你的scale选的很小,比如:0.0000000(n个0)001,但是得到的结果还是很大,这里做了一个实验:一个10M左右的图片,处理后大小为2M多。有点像是“压不动”的感觉。当然如果是小图片的话那就是没问题,能满足你的希望的压缩到的大小。
既然是循环压,那么就要说一下算法。考虑到递归,二分法,后来发现网上也是有的,二分法处理:更快一点压缩图片到指定的大小。先看一段代码:
//二分最大10次,区间范围精度最大可达0.00097657;最大6次,精度可达0.015625
for (int i = 0; i < 10; ++i) {
compression = (max + min) / 2;
imageData = UIImageJPEGRepresentation(image, compression);
//容错区间范围0.9~1.0
if (imageData.length < fImageBytes * 0.9) {
min = compression;
} else if (imageData.length > fImageBytes) {
max = compression;
} else {
break;
}
}
上面就是使用二分法进行处理,比for循环依次递减“高效”很多,而且也合理很多。
这样压缩到“极致”(一般我们不用进行太多的for循环,个人觉得参数到0.05已经可以了如果还是比你想要的大很多那就不要用UIImageJPEGRepresentation了),劳民伤财,劳的是cpu的高速运转,伤的是手机老化加快。哈哈,皮一下!
3.上面也提到了压缩“压不动”怎么办?
我们其实可以换一个方式,进行尺寸压缩:
提到尺寸压缩,你会不会很失望,看你的文章,原来也是使用UIGraphicsBeginImageContextWithOptions然后drawInRect绘制一个图片大小。
代码类似如下:
/* 根据 dWidth dHeight 返回一个新的image**/
- (UIImage *)drawWithWithImage:(UIImage *)imageCope Size:(CGSize)size {
//这里设置为0,意为自动设置清晰度,图片可以是别的传过来的图片信息
UIGraphicsBeginImageContextWithOptions(size, NO,0);
[imageCope drawInRect:CGRectMake(0, 0, size.width, size.height)];
imageCope = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return imageCope;
}
首先我需要说一下这个绘制很耗内存性能的,[UIImage drawInRect:]在绘制时,先解码图片,再生成原始分辨率大小的bitmap,这是很耗内存的,并且还有位数对齐等耗时操作。如果在一个方法中循环压缩比例进行代码的比例压缩,那么这种使用UIKit类进行图片绘制的话是需要先把图片读入内存然后在进行绘制,那么势必会给内存中占用大量的临时内存bitmap,而这个如果再加上循环,那么内存占有将是不可估量的。
4.你可能会说,我加一个自动释放池@autoreleasepool,不就好了?
错:首