适用于 iOS 的 HEIC 图像压缩

HEIC 图像压缩

JPEG IFF 格式通常可能的格式。JPEG 格式文件类型可能是JPEG 格式的文件。

HEIF 或多种图像文件格式,在许多方面都将是一种 JPEG 前身。格式由 2013 年,名称类型可以替代它的格式,支持 JPEG 的数据的图像数据,包括:

  • 项目
  • 顺序
  • 推导
  • 元数据
  • 辅助图像项

这些数据类型 HEIF 可以比 JPEG存储最新的最新数据更多。这个图像编辑图像使用)示例(例如存储的非常有用。您还可以存储 iPhone 上记录的)

MPEG 规范中定义了扩展名。对于他们的 HEIF 文件,Apple 决定使用**.ic**扩展名,它由多种图像容器代表。他们的选择代表设备使用 HEVC 解码器的文件,但 Apple 也可以读取其他文件一些编解码器压缩的文件。

入门

要开始使用,请您收藏本教程顶部或下载的材料按钮。在 zip 文件中,找到两个文件夹的FinalStarter

这是一个简单的示例应用程序,显示两个图像视图和一个用于调整图片的 JPEG 和他的图片展示项目的标签。 ,所有这些都形成了一个无功能的功能。

此应用程序的目标是通过显示图像压缩所需的时间以及 HEIC 文件的大小来展示使用 HEIC 与 JPEG 的优势。它还展示了如何使用共享表共享 HEIC 文件。

另存为HEIC

打开启动项目后,并运行以查看应用程序的 UI 运行情况。

image.png

在开始压缩图像之前,您需要选择图像。Jeremy Thomas在 Unsplash的默认图片很不错,但最好看看它是如何在您自己的内容上工作的。

在MainViewController.swift中,将以下内容添加到底部文件:

扩展MainViewController : UIImagePickerControllerDelegate ,
                               UINavigationControllerDelegate {
   func  imagePickerControllerDidCancel ( _picker : UIImagePickerController ) {
     // 1
    picker.dismiss(动画:真)
  }
  
  功能图像选择控制器(
    _选择器:UIImagePickerController,
    didFinishPickingMediaWithInfo
    信息:[ UIImagePickerController . InfoKey:任意]
    ) {
    picker.dismiss(动画:真)
    
    // 2
    守卫让 image = info[.originalImage] as?  UIImage其他{
      返回
    }
    
    // 3
    原始图像=图像
    更新图像()
  }
  
}
复制代码

这是一个简单的实现UIImagePickerControllerDelegate。你在这里:

  1. 单击取消按钮时关闭选择器。
  2. 从选择器中获取原始图像,以便在此应用中获得最佳效果。
  3. 存储此图像并更新图像视图。

现在updateImages()什么都不做。接下来,将这些行添加到空addButtonPressed()

让选择器=  UIImagePickerController ()
picker.delegate =  self

礼物(选择器,动画:真)
复制代码

这是一个图像才能选择器,让用户可以自己选择。但是,您仍然需要更新图像才能正常工作。

compressJPGImage(with:)用以下内容替换和空实现compressHEICImage(with:)

private  func  compressJPGImage(质量:CGFloat) {
  jpgImageView.image = originalImage
}

个人功能压缩HEICImage(质量:CGFloat) {
  heicImageView.image = originalImage
}
复制代码

图像视图将显示选择的图像。两个图像现在是临时的,但将验证是否正常工作。

现在,并运行应用程序。选择一个图像以查看它是否出现在两个图像视图中。

image.png

模拟出来之后就可以使用显示器了。但它还没有做任何后续的图像显示类型的强度调整。

在上显示此问题时,您需要在设备上显示更多类似的问题。

首先在MainViewController.swift的顶部添加以下内容originalImage

var previousQuality : Float  =  0
复制代码

将存储一个最常见的属性,因此您将使用它来根据这个属性的更新次数。

,在 MainViewController 部分的下面添加两个方法:

@objc私有函数调用(){
  更新图像()
}

@objc私有属性IDChange () {
  让 diff =  abs (compressionSlider.value - previousQuality)
  
  守卫差异>  0.1否则{
    返回
  }
  
  previousQuality = compressionSlider.value
  
  更新图像()
}
复制代码

这个方法的每日更新次数是最新的。

在底部viewDidLoad()添加以下内容:

压缩目标.addTarget(
  自己,
  动作:#selector(sliderEndedTouch),
  对于:[.touchUpInside,.touchUpOutside]
)
复制代码

用它来实现这个目标,在流行的背景下,在流行的背景下流行起来。

这些,终于到了开始压缩这些图像的时候了。

在MainViewController.swift的顶部添加以下属性:

小包压缩队列=  OperationQueue (私有)
复制代码

操作可能是一种方法的繁重工作,应用程序的其他部分。确保开始执行还可以取消任何新的活动。例如,在取消当前任务之前,这是一项任务。

resetLabels()在调用里面之后添加以下行updateImages()

压缩机会.cancelAllOperations()
复制代码

这会在新任务取消之前没有会中添加的所有操作。如果这一步,您可能会在查看中设置错误压缩质量的图像。

将以下内容替换为compressJPGImage(with:)以下内容:

// 1 
jpgImageView.image =无
jpgActivityIndi​​cator.startAnimating()

// 2
压缩可能.addOperation {
  // 3守卫让数据=  self 
  .originalImage.jpegData (compressionQuality: quality) else {
    返回
  }
  
  // 4 
  DispatchQueue .main.async {
     self .jpgImageView.image =  UIImage(数据:数据)
    // TODO:在此处添加图像大小... 
    // TODO:在此处添加压缩时间... 
    // TODO :在此处举办分享按钮...
    
    UIView .animate(withDuration: 0.3 ) {
       self .jpgActivityIndi​​​​cator.stopAnimating()
    }
  }
}
复制代码

使用的代码,您:

  1. 删除旧图像并启动活动指示器。
  2. 将压缩任务添加到定义的操作队列中。
  3. 使用质量参数压缩原始图像并将其转换为Data.
  4. 从压缩数据创建一个 UIImage 并在主线程上更新图像视图。请记住,UI 操作应始终发生在主线程上。您将很快向此方法添加更多代码。

这就是使用 JPEG 编解码器压缩图像的过程。要添加 HEIC 图像压缩,请将以下内容替换为compressHEICImage(with:)

heicImageView.image =  nil
heicActivityIndi​​cator.startAnimating()

压缩队列.addOperation {
  做{
    让数据= 尝试 自我.originalImage.heicData(compressionQuality:质量)
    
    DispatchQueue .main.async {
       self .heicImageView.image =  UIImage (data: data)
       // TODO:在此处添加图像大小... 
      // TODO:在此处添加压缩时间... 
      // TODO:在此处禁用共享按钮.. .
      
      UIView .animate(withDuration: 0.3 ) {
         self .heicActivityIndi​​cator.stopAnimating()
      }
    }
  } catch {
     print ( "创建 HEIC 数据时出错:\(error.localizedDescription) " )
  }
}
复制代码

HEIC 图像压缩方法只有一个区别。UIImage+Additions.swift图像数据在当前为空的辅助方法中压缩。

打开UIImage+Additions.swift你会发现一个空的heicData(compressionQuality:). 在添加方法的内容之前,您需要一个自定义错误类型。

在扩展的顶部添加以下内容:

枚举 HEICError :错误{
   case heicNotSupported
   case cgImageMissing
   case couldNotFinalize
}
复制代码

Error枚举包含一些案例,以说明使用 HEIC 压缩图像时可能出现的各种问题。并非所有 iOS 设备都可以捕获 HEIC 内容,但大多数运行 iOS 11 或更高版本的设备都可以读取和编辑此内容。

将内容替换为heicData(compressionQuality:)

// 1 
let data =  NSMutableData ()
 guard  let imageDestination = 
  CGImageDestinationCreateWithData (
    数据,AVFileType .heic作为 CFString,1,nil
  )
  否则{
    抛出 HEICError .heicNotSupported
}

// 2 
guard  let cgImage =  self .cgImage else {
   throw  HEICError .cgImageMissing
}

// 3 个
让选项:NSDictionary  = [
  kCGImageDestinationLossyCompressionQuality:compressionQuality
]

// 4 
CGImageDestinationAddImage (imageDestination, cgImage, options)
 guard  CGImageDestinationFinalize (imageDestination) else {
   throw  HEICError .couldNotFinalize
}

将数据作为 数据返回
复制代码

是时候打破这个了:

  • 首先,您需要一个空的数据缓冲区。此外,您还可以使用CGImageDestinationCreateWithData(_:_:_:_:). 此方法是图像 I/O框架的一部分,充当一种容器,可以在写入图像数据之前添加图像数据并更新其属性。如果此处出现问题,则说明 HEIC 在设备上不可用。
  • 您需要确保有图像数据可供使用。
  • 传递给方法的参数使用 key 应用kCGImageDestinationLossyCompressionQuality。您正在使用该NSDictionary类型,因为CoreGraphics需要它。
  • 最后,将图像数据与选项一起应用到目的地。CGImageDestinationFinalize(_:)完成 HEIC 图像压缩,true如果成功则返回。

构建并运行。您现在应该看到图像将根据滑块的值*发生变化。*底部图像应该需要更长的时间才能出现,因为 HEIC 图像压缩涉及更多,因为它可以节省更多磁盘空间。

image.png

测量时间

现在,您可能会认为整个 HEIC 事情并不令人印象深刻。目前唯一清楚的是,使用 HEIC 压缩图像很慢。好吧,接下来你会看到 HEIC 文件要小多少。

项目中包含一个名为Data+Additions.swift的帮助文件,其中包含一个计算属性,可以漂亮地打印对象的大小Data。此属性使用Foundation框架的便捷ByteCountFormatter来格式化字节大小。

MainViewController.swift中,将TODO: Add image size here... insidecompressJPGImage(with:)替换为:

自我.jpgSizeLabel.text = data.prettySize
复制代码

与 JPEG 方法一样,将TODO: Add image size here... insidecompressHEICImage(with:)替换为:

自我.heicSizeLabel.text = data.prettySize
复制代码

这将更新尺寸标签以反映每个图像的尺寸。

构建并运行。您应该立即看到使用 HEIC 可以节省多少空间,这更有用。

image.png

在 HEIC 和 JPEG 之间进行选择时要考虑的最后一个因素是时间。压缩所需的时间是需要考虑的关键数据。如果您的应用程序需要空间速度,那么 HEIC 可能不是您的最佳选择。

MainViewController.swift的顶部添加以下内容:

私有 让numberFormatter =  NumberFormatter ()
复制代码

格式化程序有助于使数字更具可读性。此格式化程序将使读取精确的时间间隔变得更容易。

在 的底部viewDidLoad(),就在 之前updateImages(),添加以下代码:

numberFormatter.maximumSignificantDigits =  1 
numberFormatter.maximumFractionDigits =  3
复制代码

这会将格式化程序配置为限制其输出,因为两种压缩方法之间的差异是显而易见的。如果两者更接近,那么更高水平的精度将是有益的。

在之后添加以下方法resetLabels()

私人 func  elapsedTime (从 startDate : Date ) -> String ? {
   let endDate =  Date ()
   let interval = endDate.timeIntervalSince(startDate)
   let intervalNumber =  NSNumber (值:interval)
  
  返回numberFormatter.string(来自:intervalNumber)
}
复制代码

此方法使用您之前声明的格式化程序。它接受开始日期,然后根据该日期计算持续时间,并使用数字格式化程序返回一个可选字符串。

在里面compressJPGImage(with:)添加这个到方法的顶部:

让startDate = 日期()
复制代码

接下来,替换TODO: Add compression time here... inside compressJPGImage(with:)

if  let time =  self .elapsedTime(from: startDate) {
   self .jpgTimeLabel.text =  " \(time) s"
}
复制代码

立即记录开始日期,确保方法的所有部分都有助于计算的持续时间。一旦在主队列上完成解码,就会设置时间标签。

像以前一样,您需要为 HEIC 图像压缩方法添加随附的逻辑。将此添加到顶部compressHEICImage(with:)

让startDate = 日期()
复制代码

并将**TODO: Add compression time here...**替换为以下内容:

if  let time =  self .elapsedTime(from: startDate) {
   self .heicTimeLabel.text =  " \(time) s"
}
复制代码

在输入或复制类似的代码时,请确保您设置了正确的标签。请注意,这heicTimeLabel是在 HEIC 方法和jpgTimeLabelJPG 方法中设置的。

构建并运行。

image.png

现在您可以做出充分知情的决定。JPG 压缩速度非常快,但代价是图像更大。相反,HEIC 图像更小,但压缩更慢。

了解用户的设备是一件好事。由于 HEIC 需要更长的时间,因此您可能会在电池电量不足时推迟节省空间。您还可以检查设备的可用存储空间。如果磁盘已满 75%,请始终选择 HEIC 图像压缩。

共享 HEIC

最后要考虑的一件事是共享 HEIC 图像。JPEG 压缩算法多年来一直是网络上的标准,但 HEIC 必须提供的灵活性和节省空间令人印象深刻。

许多精心设计的网站已经使用 JPEG 压缩其内容。如果一个站点的文件已经很小,那么节省 50% 就没有那么诱人了。但节省一半的高质量 iPhone 照片空间更有意义。

虽然现在可能不是在网络上全面使用 HEIC 的合适时机,但有些网站提供了上传 HEIC 照片的支持。在 Apple 生态系统中,其他应用程序处理 HEIC 内容应该没有问题。共享内容时,提供共享格式的选择是有益的。

在下面添加以下方法updateImages()

私人 函数 shareImage(_ 图像:UIImage){
  让avc =  UIActivityViewController(
    活动项目:[图片],
    应用活动:无
  )
  现在(avc,动画:真)
}
复制代码

此方法共享以任一格式压缩的图像。UIImage类负责处理其底层格式,因此您不必这样做。

要使用它,请shareButtonPressed()MainViewController.swift中添加以下内容:

让avc =  UIAlertController (
  标题:“分享”,
  消息:“你想如何分享?” ,
  首选样式:.alert
)

如果 让jpgImage = jpgImageView.image {
  avc.addAction( UIAlertAction (title: "JPG" , style: .default) { _  in 
    self .shareImage(jpgImage)
  })
}

如果 让heicImage = heicImageView.image {
  avc.addAction( UIAlertAction (title: "HEIC" , style: .default) { _  in 
    self .shareImage(heicImage)
  })
}

avc.addAction( UIAlertAction (title: "Cancel" , style: .cancel, handler: nil ))

现在(avc,动画:真)
复制代码

这为此应用程序设置了简单的共享功能,因为图像视图中已经有两个压缩图像。对于将所有图像存储为 HEIC 的真实应用程序,您需要在共享之前将图像转换为 JPEG。

添加到示例应用程序的最后一项生活质量功能是在没有可用图像时阻止共享。对于较大的文件,HEIC 图像压缩可能会失败或需要更长时间。在此期间,禁用共享按钮以避免混淆并维护良好的用户体验是有益的。

里面updateImages()添加:

导航项.leftBarButtonItem ?.isEnabled = 假
复制代码

此行在压缩开始之前禁用共享按钮。

接下来,将每次出现的**TODO: Disable share button here...**替换为:

自.navigationItem.leftBarButtonItem ?.isEnabled = 真
复制代码

每次压缩完成后,共享按钮会重新启用。如果 HEIC 图像压缩仍在处理中,您只会在警报中看到 JPG 选项。对于您的应用程序,等到这两个选项都可用可能更有意义。

构建并运行。

image.png

恭喜,示例应用程序已完成!

结论

您现在应该对 HEIC 及其优点和缺点有所了解。HEIC 有很多用例,随着时间的推移,它只会越来越受欢迎。

回顾 HEIC 的好处:

  • 与 JPEG 相比,文件大小小 50%。
  • 包含许多图像项。
  • 图像派生,非破坏性编辑。
  • 图像序列,例如实时照片。
  • 用于存储深度或 HDR 数据的辅助图像项。
  • 图像元数据,例如位置或相机信息。

下载项目资料与原文地址

这里也推荐一些面试相关的内容!

猜你喜欢

转载自juejin.im/post/7106019877391400997