iOS中的图像处理

作者: 王沐凡 | 来源:发表于2018-12-13 15:52 被阅读22次

    本文idea参考了objc期刊的部分文章,本文demo地址可以在Light & shallow中看到。

    一、图片种类

    矢量图

    iOS支持两种格式的矢量图:PDF和SVG。我们先来理解一下矢量图,它其实是一组绘图指令,并且独立于尺寸存在。最大的优点就是无论放大缩小还是旋转,都不会失真。比如在绘制图形时,如果你想扩大一个圆形,只需在绘制前扩大它的半径就可以了。位图则没这么容易,最起码,如果扩大的比例不是二的倍数,就会涉及到重绘图像。由于我们不知道这图像是一个圆形,所以无法确保弧线的准确描绘,效果看起来肯定不如按比例绘制的线条那样好。也因此,在像素密度不同的设备中,矢量图像作为图形资源会非常有用。位图的话,同样的图标,在视网膜屏幕之前的 iPhone 上看起来并没有问题,在拉伸两倍后的视网膜屏幕上看起来就会发虚。就好像仅适配了 iPhone 的 App 运行在 iPad 的 2x 模式下就不再那么清晰了。

    Xcode虽然支持PDF格式的矢量图,然而支持的却并不是那么完善,有点蒙蔽的意识。对于一个PDF图片,Xcode实际上在编译阶段对应的生成了1x,2x,3x的png图(这个1x图就是PDF图片本身),因此让开发者看起来Xcode支持了PDF的矢量图。

    位图

    大部分图像都是以位图方式处理的,如我们常见的图片格式GIF,PNG,JPEG等。GIFPNG 格式对于具有大面积相同颜色的图像是最好的选择,因为它们使用的压缩算法可以减少存储需求。这种压缩是无损的,这意味着图像质量不会被压缩影响。

    一个有损压缩图像格式的例子是 JPEG。创建 JPEG 图像时,通常会指定一个与图像质量相关的压缩比值参数,压缩程度过高会导致图像质量变差。JPEG 不适用于对比鲜明的图像 (如线条图),其压缩方式对类似区域的图像质量损害会相对严重。如果某张截图中包含了文本,且保存为 JPEG 格式,就可以清楚地看到:生成的图像中字符周围会出现杂散的像素点。在大部分照片中不存在这个问题,所以照片主要使用 JPEG 格式。

    二、图片属性(图片的元数据信息)

    用于存储图像信息的标准格式是 Exif(可交换图像文件格式)。在照片中,通常会捕获照相的日期和时间,图片方向,如果设备支持,还包括 GPS 坐标。这也就是当有人在微信朋友圈发了一张在自己家鱼缸的照片,却配文说在马尔代夫潜泳时,会被人识破的原因。

    在 iOS 中,可以使用 CGImageSourceCopyPropertiesAtIndex 方法访问 Exif 信息。这个方法会返回一个包含所有相关信息的字典。

    //1.获取图片文件
    NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"YourPic" withExtension:@""];
    //2.创建CGImageSourceRef
    CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)fileUrl, NULL);
    //3.利用imageSource获取ExifData
    CFDictionaryRef imageInfo = CGImageSourceCopyPropertiesAtIndex(imageSource, 0,NULL);
    NSDictionary *exifDic = (__bridge NSDictionary *)CFDictionaryGetValue(imageInfo, kCGImagePropertyExifDictionary) ;
    

    三、图片处理框架FrameWork

    在 iOS 中有几个类是用来处理位图数据的:UIImage (UIKit),CGImage (Core Graphics) 和 CIImage (Core Image)。在创建以上类的实例之前,由 NSData 持有实际的数据。获得一个 UIImage 最简单的方法是使用 imageWithContentsOfFile: 方法,或者根据其来源,也可以使用 imageWithCGImage:,imageWithCIImage: 或者 imageWithData:。有多个不同却相似的类看起来有点多余,部分原因是因为它们来自不同的框架,对于图像储存的优化方式也各有侧重,通常情况下,不同类型之间是可以轻松转换的。

    在iOS8之前,开发者只能使用AssetsLibrary框架访问用户相册库,而近几年,由于照片应用增加了很多新的特性,AssetsLibrary框架已经无法满足需求,于是PhotoKit框架应运而生。PhotoKit与设备照片库是无缝对接的,就是说PhotoKit 定义了与系统的 Photos 应用内展现给用户的模型对象相一致的实体图表。

    PhotoKit对象模型

    所有的 PhotoKit 对象都是继承自 PHObject 抽象基类,其公共接口只提供了一个 localIdentifier 属性。

    PHOTOS_CLASS_AVAILABLE_IOS_TVOS(8_0, 10_0) @interface PHObject : NSObject <NSCopying>
    
    // Returns an identifier which persistently identifies the object on a given device
    @property (nonatomic, copy, readonly) NSString *localIdentifier;
    
    @end
    

    我们来看几个经常用到的类:
    PHAsset 表示用户照片库中一个单独的资源,用以提供资源的元数据。

    成组的资源叫做资源集合,用 PHAssetCollection 类表示。一个单独的资源集合可以是照片库中的一个相册或者一个时刻,或者是一个特殊的“智能相册”。这种智能相册包括所有的视频集合,最近添加的项目,用户收藏,所有连拍照片等等,PHAssetCollectionPHCollection 的子类。

    PHCollectionList 表示一组的 PHCollections。因为它本身就是 PHCollection,所以集合列表可以包含其他集合列表,它们允许复杂的集合继承。实际上,我们可以在照片应用的时刻栏目中看到它:照片 --- 时刻 --- 精选 --- 年度,就是一个例子。

    获取 (Fetch) 照片实体

    我们知道通过AssetLibrary框架获取资源的方式是通过枚举用户资源库,然后进行遍历匹配来得到,这不得不说非常低效。

    而PhotoKit 实体的实例是通过获取得到的。

    获取请求

    获取操作是由上面描述的实体的类方法实现的。要使用哪个类/方法,取决于问题所在范围和你展示与遍历照片库的方式。所有获取方法的命名都是相似的:class func fetchXXX(..., options: PHFetchOptions) -> PHFetchResultoptions 参数给了我们一个对结果进行过滤和排序的途径,这和 NSFetchRequestpredicatesortDescriptors 参数类似。

    获取结果

    你可能已经注意到了这些获取操作不是异步的。它们返回了一个 PHFetchResult 对象,可以用类似 NSArray的接口来访问结果内的集合。它会按需动态加载内容并且缓存最近请求的内容。这个行为和设置了 batchSize属性的 NSFetchRequest 返回的结果数组相似。对于 PHFetchResult 来说,没有办法用参数来指定这个行为,但是官网文档保证 “即使在处理大量的返回结果时,依然能够有最好的表现”。

    就算满足请求的照片库内容发生了改变,获取方法所返回的 PHFetchResult 对象是不会自动更新。
    这一部分的代码你可以在Light & shallow中看到。

    四、图片操作

    相关文章

      网友评论

        本文标题:iOS中的图像处理

        本文链接:https://www.haomeiwen.com/subject/vulvhqtx.html