Swift图片压缩处理

由于项目中有大量的上传图片,之前图片的处理方式直接就用了UIImageJPEGRepresentation方式来处理,也用了很长的一段时间,后面由于权限的开放,从相册选择的使用频率变高,由于图片的来源不一,所以相片的质量不一样,有的是客户从其他渠道传输过来的,肯定也经过了压缩的处理,所以再经过我们这边统一的方法再去压缩的话,将会导致上传的图片失真,不清晰。针对以上痛点,于是有了下面的处理方式.

压缩方式1:

循环压缩法

   /// 压缩图片数据-不压尺寸
    ///
    /// - Parameters:
    ///   - maxLength: 最大长度
    /// - Returns:
    func compressImageOnlength(maxLength: Int) -> Data? {
        
        guard let vData = UIImageJPEGRepresentation(self, 1) else { return nil }
        XYJLog(message: "压缩前kb: \( Double((vData.count)/1024))")
        if vData.count < maxLength {
            return vData
        }
        var compress:CGFloat = 0.9
        guard var data = UIImageJPEGRepresentation(self, compress) else { return nil }
        while data.count > maxLength && compress > 0.01 {
            XYJLog(message: "压缩比: \(compress)")
            compress -= 0.02
            data = UIImageJPEGRepresentation(self, compress)!
        }
        XYJLog(message: "压缩后kb: \(Double((data.count)/1024))")
        return data
    }

由于我们的需求是最多可以五张图片拼接处理,最后压缩的时候,采用此方法,可能会导致压缩时间比较久,用户体验不友好,于是采用了方式二

压缩方式2:

二分压缩法

     //二分压缩法
     func compressImageMid(maxLength: Int) -> Data? {
        var compression: CGFloat = 1
        guard var data = UIImageJPEGRepresentation(self, 1) else { return nil }
        XYJLog(message: "压缩前kb: \( Double((data.count)/1024))")
        if data.count < maxLength {
            return data
        }
        print("压缩前kb", data.count / 1024, "KB")
        var max: CGFloat = 1
        var min: CGFloat = 0
        for _ in 0..<6 {
            compression = (max + min) / 2
            data = UIImageJPEGRepresentation(self, compression)!
            if CGFloat(data.count) < CGFloat(maxLength) * 0.9 {
                min = compression
            } else if data.count > maxLength {
                max = compression
            } else {
                break
            }
        }
        var resultImage: UIImage = UIImage(data: data)!
        if data.count < maxLength {
            return data
        }

方式一、二调用的方式如下

// 压缩到1M 以内
 var  imageData = uploadsizeImg?.compressImageMid(maxLength: 1024*1024)

采用二分压缩法在效率上有一定的提升,但是对于五张图片合成后,如果尺寸越大,越越容易导致内存出现问题,严重的将会导致崩溃,所以在压缩数据前我们需要对合成的图片做尺寸上的压缩

尺寸压缩法

   /// 根据尺寸重新生成图片
    ///
    /// - Parameter size: 设置的大小
    /// - Returns: 新图
    public func imageWithNewSize(size: CGSize) -> UIImage? {
    
        if self.size.height > size.height {
            
            let width = size.height / self.size.height * self.size.width
            
            let newImgSize = CGSize(width: width, height: size.height)
            
            UIGraphicsBeginImageContext(newImgSize)
            
            self.draw(in: CGRect(x: 0, y: 0, width: newImgSize.width, height: newImgSize.height))
            
            let theImage = UIGraphicsGetImageFromCurrentImageContext()
            
            UIGraphicsEndImageContext()
            
            guard let newImg = theImage else { return  nil}
            
            return newImg
            
        } else {
            
            let newImgSize = CGSize(width: size.width, height: size.height)
            
            UIGraphicsBeginImageContext(newImgSize)
            
            self.draw(in: CGRect(x: 0, y: 0, width: newImgSize.width, height: newImgSize.height))
            
            let theImage = UIGraphicsGetImageFromCurrentImageContext()
            
            UIGraphicsEndImageContext()
            
            guard let newImg = theImage else { return  nil}
            
            return newImg
        }
    
    }

图片合成问题补充

1.  合成图片:由于上传确认页面的图片可以点击进去编辑图片,或者添加图片,如果这里每添加一张图片或者合成一张图片的话,会相当的耗费性能,所以合成图片的操作放在点击上传按钮那里
点击上传调用合成图片方法,合成图片方法主要分两步,实现思路如下:
(1)创建一个contentView用来装多个ImageView,根据图片数组的数量for循环来添加imageview,imageview设置图片的时候先对图片进行数据压缩处理,然后再对尺寸进行压缩处理,下面是压缩处理规则:
if imagedata.count <  0.8M   {  
不压缩
} else if imagedata.count >  0.8M && imagedata.count <1M {  
压缩系数0.8获取图像
} else if imagedata.count >  1M && imagedata.count <2M {  
压缩系数0.3获取图像
} else {
压缩系数0.1获取图像
}

压缩完数据以后再进行尺寸压缩
if 图片宽度>两倍屏幕宽 || 图片高度>两倍屏幕高 {
 图片大小缩放系数0.5处理
}
缩放之后设置到imageView上
(2)通过for循环来获取contentView上的imageView上的图片,根据已经处理好的image来设置imageview的frame,排好版面到contentView上,最后就是将contentView转化成image,并且赋值给合成图片变量composeImage
以上两个for循环中的内容置于autoreleasepool中
整个上传流程
点击上传 ->进入合成图片方法,显示正在加载的圈圈,延时1.5秒进入上传方法 -> 二分法压缩图片数据到1M以内,图片大于3张延时3秒,否则2秒,延时之后执行隐藏压缩圈圈方法,发起正式上传网络请求 -> 上传成功之后,图片变量置为nil,移除图片数组元素,返回上一级,查看deinit打印执行


测试与分析
6测试---这里还是之前的合成图片后,再对合成图片压缩尺寸
如果一进入上传页面页面显示且绘制合成图片51 -> 61  大概增加10M到150M
五张合成后再缩放图片:大概增加100 到150M
单独测试压缩方法  增加大概100M
单独测试压缩方法加上传 增加大概110M
缩放图片尺寸加压缩 测试大概增加150M
缩放 压缩  上传一气呵成大概也是增加150M左右
综上最耗内存的两个方法 一个是缩放尺寸(会进行绘制)   一个是压缩
大小手机测试:
5s 上传7+拍的5张照片 整个上传过程 最高出现增加350M
5s 上传自己拍的5张  最高出现增加240M   

分析:
1.在几个for循环里面加个自动释放池感觉没有缓解什么,几M的区别
2.缩放的的图片被压缩成data后,马上置为nil 整个上传过程增加100-130  所以这里有一定的作用
3.  有的图片的长宽高达屏幕的5,6倍,所以在合成之前,我们可以把每一张图片缩放一半,后面整张合成的图片将不再进行缩放,经测试这样合成图片,再去上传图片,我用5s上传7+拍的图片大概增加120M,相比以前的240,350节约一大半的内存,可以有效防止内存不足闪退。

总结:经过以上的方法处理,图片的质量和内存有了很大的改善,值得注意的是,我们在处理图片的时候,一定要注意测试内存,如果内存没有释放,将随着拍摄次数的增多,内存逐渐增大,最后的结果就是崩溃,可能客户跟你反馈,你还会一脸懵逼,当然知道这个问题就好办了,这种情况一般是图片没有释放,或者代码里有循环引用所致,解决好就可以了。

你可能感兴趣的:(Swift图片压缩处理)