图片对象是在开发过程中经常需要处理的对象之一,经常需要对图片进行压缩处理,而如何快速高效地处理图片数据,是我们应该关心的重要问题之一.
在实际开发中,我们压缩图片的手段主要有两种,一种是压缩图片的质量,另一种是压缩图片的质量.
1.1 压缩图片质量的原因
节约存储空间:存储空间有限或者价格比较昂贵等原因导致没有足够的存储空间去储存图片;
加快传输速度,提升用户体验:本身对于图片质量没有特别高的需求,压缩图片质量可以加快客户端与服务器的交互响应时间提升用户体验;
某些特殊场景的需求:在一些场景下不得不进行图片压缩.例如使用极光进行微信分享时要求缩略图的大小不能超过32K;
其他场景.
1.2 压缩图片的时机
- 在拍照时定义AVCaptureSession的sessionPreset属性,可以控制拍摄图片的质量,详情可以参考apple官方文档;
- 在获取到图片对象以后,使用相关api进行压缩.以下我们主要对这一部分进行描述.
1.3 压缩原理
- 在压缩过程主要通过UIImageJPEGRepresentation实现;
- 由于compressionQuality和返回NSData对象的大小并没有函数关系,所以为了更加精确接近我们需要的结果,我们采用二分法来靠近计算结果.
1.4 算法实现
我们假定将图片压缩到不大于masSize,大于0.9 * maxSize大小即符合要求,我们使用图片做测试,设定最终要求的图片大小maxSize=30KB.
运行结果如下:
我们发现:
这个api在图片质量被压缩到一定程度之后,随着compressionQuality参数变小,图片的质量不再发生变化.需要我们需要添加退出循环压缩的第一个终止条件:当压缩之后如果NSData的大小不再发生变化时, 终止循环.
运行结果如下:
所以加上第一个终止条件之后,当返回的NSData对象与上次相同时,及时终止了循环,防止出现死循环的情况.
现在我们来审视一下,这个代码是不是已经可以应对所有的情况了呢?
- 我们的算法使用的二分法来逐渐逼近我们想要的目标值,所以在某些情况下就会出现为了逼近某个对我们来说不那么重要的最终值(或者是一个无法达到的极限值)而进行许多次循环情况,比如为了两次运算相差了0.00000000001的精确度,却还在为了接近目标值而进行循环.这种情况对于像素要求不高的操作来说没有太大实际意义,但是却浪费了很多内存;
- 如果某个目标值需要的循环次数比较多,就有可能造成内存被耗尽,所以我们使用手动添加一个自动释放池来平稳释放掉局部变量占用内存.
修改之后的代码大概这个样子,就基本上可以满足我们的需求.
2.压缩图片的尺寸
2.1 压缩图片尺寸的原因
- 为了更好的使用自适应进行UI适配:有时候为了使用控件的固有属性来做图片展示适应,就需要控制图片本身的尺寸;
- 使用统一的尺寸标准:移动端设备多种多样,为了各个移动端传回的图片标注一致,需要对图片的尺寸做统一处理;
- 加快传输速度,提升用户体验: 业务需求对图片本身的尺寸要求不高的情况下,可以适当控制图片尺寸,来减小图片的大小,加快上传/下载速度,提升用户体验.
- 某些特殊场景的需求:在一些场景下不得不进行图片压缩.例如使用极光进行微信分享时要求缩略图的大小不能超过32K等;
- 其他原因.
2.2 压缩图片尺寸的时机
- 在移动端拍摄图片时定义,定义相机AVCaptureVideoPreviewLayer预览图层的大小来限制图片的尺寸;
- 在获取到图片对象之后,使用UIGraphics框架中的相关方法进行重绘.下面的讨论主要针对这一类型.
2.3 图片尺寸压缩原理
主要是用UIGraphics中的两个C函数来实现:
- UIGraphicsBeginImageContext(或者UIGraphicsBeginImageContextWithOptions)初始化绘图上下文;
- 使用UIImage中的drawInRect:在指定的区域内重新绘制图片;
- 使用UIGraphicsGetImageFromCurrentImageContext获取重绘之后的新图片;
- 使用UIGraphicsEndImageContext关闭上下文.
2.4 算法实现
假设我们需要通过压缩图片的尺寸来使当前的图片对象达到占据空间不大于30K的需求,同样使用二分法来进行循环压缩.
3 同时使用两种压缩方式
在很多使用场景下,单一的压缩方式有时候不能保证达到需求(比如单一使用压缩图片质量达到指定物理大小之后发现图片根本无法识别图片内容等状况),这时候就需要根据实际的情况交替使用两种压缩方达到目的(比如先使用质量压缩到能够识别的尺寸,再逐渐使用压缩质量尝试做进一步压缩),具体需求需要具体分析.