新闻资讯

    iOS如何实现图片压缩

    本文讲解"iOS怎么实现图片压缩",用于解决相关问题。

    两种压缩图片的方法:压缩图片质量(),压缩图片尺寸(Size)。

    压缩图片质量

    NSData *data = UIImageJPEGRepresentation(image, compression);
    UIImage *resultImage = [UIImage imageWithData:data];

    通过 和 的相互转化,减小 JPEG 图片的质量来压缩图片。ation:: 第二个参数 取值 0.0~1.0,值越小表示图片质量越低,图片文件自然越小。

    压缩图片尺寸

    UIGraphicsBeginImageContext(size);
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    resultImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    给定所需的图片尺寸 size, 即为原图 image 绘制为 size 大小的图片。

    压缩图片使图片文件小于指定大小

    如果对图片清晰度要求不高,要求图片的上传、下载速度快的话,上传图片前需要压缩图片。压缩到什么程度要看具体情况,但一般会设定一个图片文件最大值,例如 100 KB。可以用上诉两种方法来压缩图片。假设图片转化来的 对象为 data,通过data.即可得到图片的字节大小。

    压缩图片质量

    比较容易想到的方法是ios是否会压缩jpg格式,通过循环来逐渐减小图片质量,直到图片稍小于指定大小()。

    + (UIImage *)compressImageQuality:(UIImage *)image toByte:(NSInteger)maxLength {
     CGFloat compression = 1;
     NSData *data = UIImageJPEGRepresentation(image, compression);
     while (data.length > maxLength && compression > 0) {
     compression -= 0.02;
     data = UIImageJPEGRepresentation(image, compression); // When compression less than a value, this code dose not work
     }
     UIImage *resultImage = [UIImage imageWithData:data];
     return resultImage;
    }

    这样循环次数多,效率低,耗时长。

    可以通过二分法来优化。

    + (UIImage *)compressImageQuality:(UIImage *)image toByte:(NSInteger)maxLength {
     CGFloat compression = 1;
     NSData *data = UIImageJPEGRepresentation(image, compression);
     if (data.length < maxLength) return image;
     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;
     }
     }
     UIImage *resultImage = [UIImage imageWithData:data];
     return resultImage;
    }

    当图片大小小于 ,大于 * 0.9 时,不再继续压缩。最多压缩 6 次,1/(2^6) = 0. < 0.02,也能达到每次循环 减小 0.02 的效果。这样的压缩次数比循环减小 少,耗时短。需要注意的是,当图片质量低于一定程度时,继续压缩没有效果。也就是说, 继续减小,data 也不再继续减小。压缩图片质量的优点在于,尽可能保留图片清晰度,图片不会明显模糊;缺点在于ios是否会压缩jpg格式,不能保证图片压缩后小于指定大小。

    压缩图片尺寸

    与之前类似,比较容易想到的方法是,通过循环逐渐减小图片尺寸,直到图片稍小于指定大小()。具体代码省略。同样的问题是循环次数多,效率低,耗时长。可以用二分法来提高效率,具体代码省略。这里介绍另外一种方法,比二分法更好,压缩次数少,而且可以使图片压缩后刚好小于指定大小(不只是 < , > * 0.9)。

    + (UIImage *)compressImageSize:(UIImage *)image toByte:(NSUInteger)maxLength {
     UIImage *resultImage = image;
     NSData *data = UIImageJPEGRepresentation(resultImage, 1);
     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))); // Use NSUInteger to prevent white blank
     UIGraphicsBeginImageContext(size);
     // Use image to draw (drawInRect:), image is larger but more compression time
     // Use result image to draw, image is smaller but less compression time
     [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
     resultImage = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();
     data = UIImageJPEGRepresentation(resultImage, 1);
     }
     return resultImage;
    }

    [ :(0, 0, size.width, size.)];是用新图 绘制,也可以用原图 image 来绘制。用原图绘制,压缩后图片更接近指定大小,但是压缩次数较多,耗时较长。一张大小为 6064 KB 的图片,压缩图片尺寸,原图绘制与新图绘制结果如下

    指定大小(KB)原图绘制压缩后大小(KB)原图绘制压缩次数新图绘制压缩后大小(KB)新图绘制压缩次数

    500

    498

    6

    498

    3

    300

    299

    4

    296

    3

    100

    99

    5

    98

    3

    50

    49

    6

    48

    3

    两种绘制方法压缩后大小很接近,与指定大小也很接近,但原图绘制压缩次数可达到新图绘制压缩次数的两倍。建议使用新图绘制,减少压缩次数。压缩后图片明显比压缩质量模糊。

    需要注意的是绘制尺寸的代码

    size = (()(.size.width * sqrtf(ratio)), ()(.size. * sqrtf(ratio)));,每次绘制的尺寸 size,要把宽 width 和 高 转换为整数,防止绘制出的图片有白边。

    压缩图片尺寸可以使图片小于指定大小,但会使图片明显模糊(比压缩图片质量模糊)。

    两种图片压缩方法结合

    如果要保证图片清晰度,建议选择压缩图片质量。如果要使图片一定小于指定大小,压缩图片尺寸可以满足。对于后一种需求,还可以先压缩图片质量,如果已经小于指定大小,就可得到清晰的图片,否则再压缩图片尺寸。

    + (UIImage *)compressImage:(UIImage *)image toByte:(NSUInteger)maxLength {
     // Compress by quality
     CGFloat compression = 1;
     NSData *data = UIImageJPEGRepresentation(image, compression);
     if (data.length < maxLength) return image;
     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;
     }
     }
     UIImage *resultImage = [UIImage imageWithData:data];
     if (data.length < maxLength) return resultImage;
     // Compress by size
     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))); // Use NSUInteger to prevent white blank
     UIGraphicsBeginImageContext(size);
     [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
     resultImage = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();
     data = UIImageJPEGRepresentation(resultImage, compression);
     }
     return resultImage;
    }

    下面看下iOS图片压缩上传的实现方法

    需求

    很多时候我们上传图片经常遇到一些问题,要不就是图片质量变差,要不就是图片太大等等问题。这里,我找到了一个算是目前比较符合需求的解决方案。在原有基础上增加了动态压缩系数,改写成Swift版本。

    实现思路

    先调整分辨率,分辨率可以自己设定一个值,大于的就缩小到这分辨率,小余的就保持原本分辨率。然后再根据图片最终大小来设置压缩比,比如传入 = 30KB,最终计算大概这个大小的压缩比。基本上最终出来的图片数据根据当前分辨率能保持差不多的大小同时不至于太模糊,跟微信,微博最终效果应该是差不多的,代码仍然有待优化!

    实现代码

    .0之前旧版本压缩模式

    // MARK: - 降低质量
     func resetSizeOfImageData(source_image: UIImage, maxSize: Int) -> NSData {
      //先调整分辨率
      var newSize = CGSize(width: source_image.size.width, height: source_image.size.height)
      let tempHeight = newSize.height / 1024
      let tempWidth = newSize.width / 1024
      if tempWidth > 1.0 && tempWidth > tempHeight {
       newSize = CGSize(width: source_image.size.width / tempWidth, height: source_image.size.height / tempWidth)
      }
      else if tempHeight > 1.0 && tempWidth < tempHeight {
       newSize = CGSize(width: source_image.size.width / tempHeight, height: source_image.size.height / tempHeight)
      }
      UIGraphicsBeginImageContext(newSize)
      source_image.drawAsPatternInRect(CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
      let newImage = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()
      //先判断当前质量是否满足要求,不满足再进行压缩
      var finallImageData = UIImageJPEGRepresentation(newImage,1.0)
      let sizeOrigin  = Int64((finallImageData?.length)!)
      let sizeOriginKB = Int(sizeOrigin / 1024)
      if sizeOriginKB <= maxSize {
       return finallImageData!
      }
      //保存压缩系数
      let compressionQualityArr = NSMutableArray()
      let avg = CGFloat(1.0/250)
      var value = avg
      for var i = 250; i>=1; i-- {
       value = CGFloat(i)*avg
       compressionQualityArr.addObject(value)
      }
      //调整大小
      //说明:压缩系数数组compressionQualityArr是从大到小存储。
      //思路:折半计算,如果中间压缩系数仍然降不到目标值maxSize,则从后半部分开始寻找压缩系数;反之从前半部分寻找压缩系数
      finallImageData = UIImageJPEGRepresentation(newImage, CGFloat(compressionQualityArr[125] as! NSNumber))
      if Int(Int64((UIImageJPEGRepresentation(newImage, CGFloat(compressionQualityArr[125] as! NSNumber))?.length)!)/1024) > maxSize {
       //拿到最初的大小
       finallImageData = UIImageJPEGRepresentation(newImage, 1.0)
       //从后半部分开始
       for idx in 126..<250 {
        let value = compressionQualityArr[idx]
        let sizeOrigin = Int64((finallImageData?.length)!)
        let sizeOriginKB = Int(sizeOrigin / 1024)
        print("当前降到的质量:\(sizeOriginKB)")
        if sizeOriginKB > maxSize {
         print("\(idx)----\(value)")
         finallImageData = UIImageJPEGRepresentation(newImage, CGFloat(value as! NSNumber))
        } else {
         break
        }
       }
      } else {
       //拿到最初的大小
       finallImageData = UIImageJPEGRepresentation(newImage, 1.0)
       //从前半部分开始
       for idx in 0..<125 {
        let value = compressionQualityArr[idx]
        let sizeOrigin = Int64((finallImageData?.length)!)
        let sizeOriginKB = Int(sizeOrigin / 1024)
        print("当前降到的质量:\(sizeOriginKB)")
        if sizeOriginKB > maxSize {
         print("\(idx)----\(value)")
         finallImageData = UIImageJPEGRepresentation(newImage, CGFloat(value as! NSNumber))
        } else {
         break
        }
       }
      }
      return finallImageData!
     }

    .0版本二分法压缩模式

    // MARK: - 降低质量
    func resetSizeOfImageData(source_image: UIImage!, maxSize: Int) -> NSData {
     //先判断当前质量是否满足要求,不满足再进行压缩
     var finallImageData = UIImageJPEGRepresentation(source_image,1.0)
     let sizeOrigin  = finallImageData?.count
     let sizeOriginKB = sizeOrigin! / 1024
     if sizeOriginKB <= maxSize {
      return finallImageData! as NSData
     }
     //先调整分辨率
     var defaultSize = CGSize(width: 1024, height: 1024)
     let newImage = self.newSizeImage(size: defaultSize, source_image: source_image)
     finallImageData = UIImageJPEGRepresentation(newImage,1.0);
     //保存压缩系数
     let compressionQualityArr = NSMutableArray()
     let avg = CGFloat(1.0/250)
     var value = avg
     var i = 250
     repeat {
      i -= 1
      value = CGFloat(i)*avg
      compressionQualityArr.add(value)
     } while i >= 1
     /*
      调整大小
      说明:压缩系数数组compressionQualityArr是从大到小存储。
      */
     //思路:使用二分法搜索
     finallImageData = self.halfFuntion(arr: compressionQualityArr.copy() as! [CGFloat], image: newImage, sourceData: finallImageData!, maxSize: maxSize)
     //如果还是未能压缩到指定大小,则进行降分辨率
     while finallImageData?.count == 0 {
      //每次降100分辨率
      if defaultSize.width-100 <= 0 || defaultSize.height-100 <= 0 {
       break
      }
      defaultSize = CGSize(width: defaultSize.width-100, height: defaultSize.height-100)
      let image = self.newSizeImage(size: defaultSize, source_image: UIImage.init(data: UIImageJPEGRepresentation(newImage, compressionQualityArr.lastObject as! CGFloat)!)!)
      finallImageData = self.halfFuntion(arr: compressionQualityArr.copy() as! [CGFloat], image: image, sourceData: UIImageJPEGRepresentation(image,1.0)!, maxSize: maxSize)
     }
     return finallImageData! as NSData
    }
    // MARK: - 调整图片分辨率/尺寸(等比例缩放)
    func newSizeImage(size: CGSize, source_image: UIImage) -> UIImage {
     var newSize = CGSize(width: source_image.size.width, height: source_image.size.height)
     let tempHeight = newSize.height / size.height
     let tempWidth = newSize.width / size.width
     if tempWidth > 1.0 && tempWidth > tempHeight {
      newSize = CGSize(width: source_image.size.width / tempWidth, height: source_image.size.height / tempWidth)
     } else if tempHeight > 1.0 && tempWidth < tempHeight {
      newSize = CGSize(width: source_image.size.width / tempHeight, height: source_image.size.height / tempHeight)
     }
     UIGraphicsBeginImageContext(newSize)
     source_image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
     let newImage = UIGraphicsGetImageFromCurrentImageContext()
     UIGraphicsEndImageContext()
     return newImage!
    }
    // MARK: - 二分法
    func halfFuntion(arr: [CGFloat], image: UIImage, sourceData finallImageData: Data, maxSize: Int) -> Data? {
     var tempFinallImageData = finallImageData
     var tempData = Data.init()
     var start = 0
     var end = arr.count - 1
     var index = 0
     var difference = Int.max
     while start <= end {
      index = start + (end - start)/2
      tempFinallImageData = UIImageJPEGRepresentation(image, arr[index])!
      let sizeOrigin = tempFinallImageData.count
      let sizeOriginKB = sizeOrigin / 1024
      print("当前降到的质量:\(sizeOriginKB)\n\(index)----\(arr[index])")
      if sizeOriginKB > maxSize {
       start = index + 1
      } else if sizeOriginKB < maxSize {
       if maxSize-sizeOriginKB < difference {
        difference = maxSize-sizeOriginKB
        tempData = tempFinallImageData
       }
       end = index - 1
      } else {
       break
      }
     }
     return tempData
    }

    关于 "iOS怎么实现图片压缩" 就介绍到此。希望多多支持编程宝库。

    下一节:JVM类的加载机制原理是什么编程技术 和 程序设计

    本文讲解"JVM类的加载机制原理是什么",用于解决相关问题。一、JVM 类加载机制JVM 类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。1. 加载:加载是类加载过 ...

网站首页   |    关于我们   |    公司新闻   |    产品方案   |    用户案例   |    售后服务   |    合作伙伴   |    人才招聘   |   

地址:北京市海淀区    电话:010-     邮箱:@126.com

备案号:冀ICP备2024067069号-3 北京科技有限公司版权所有