Core Image研究
说起Core Image,恐怕不少人会觉得陌生,毕竟和Core Data、Core Animation、Core
Graphics比起来,Core
Image使用频率还是比较低的。不过,如果你的工作涉及到图像处理,除了一些第三方的框架(GPUImage之类)Core
Image还是相当不错的选择。
基本概念
CoreImage是一个图像框架,基于OpenGL顶层创建,底层则用着色器来处理图像,利用了GPU基于硬件加速来处理图像。它最大化利用其所运行之上的硬件的。每个滤镜实际上的实现,即内核,是由一个GLSL(OpenGL的着色语言)的子集来书写的。
常用类
- CIContext
上下文类,实现对图像处理的具体对象。
在Context创建的时候,可以选择使用GPU还是CPU。基于GPU的话,处理速度更快,因为利用了GPU硬件的并行优势。但是GPU受限于硬件纹理尺寸,而且如果你的程序在后台继续处理和保存图片的话,那么需要使用CPU,因为当app切换到后台状态时GPU处理会被打断。 - CIImage
CIImage是CoreImage中最基本代表图像的对象,除了包含元图像数据,还包含了作用在原图像上的滤镜链。CIImage和其他图像是不同的,在CIImage被CIContext渲染出来之前,他是依赖于滤镜链的,滤镜是不会更改CIImage中的图像数据。 - CIFilter
CIFilter用来表示CoreImage提供的各种滤镜。滤镜使用键值来设置输入值,一旦这些值设置好,CIFilter就可以用来生成新的CIImage输出图像了。但是输出的图像不会进行实际的图像渲染,他只包含一个对输入图像的引用以及需要应用与数据上的滤镜链。只要有可能,Core Image都会把工作延迟。通常情况下,直到滤镜图表的最后一个滤镜的输出被请求之前都不会发生分配或处理。
来看一个示例:
//UIImage原图
let inputUIImage = UIImage(named: "demo");
//获得原图的CIImage
let inputCIImage = CIImage(image: inputUIImage);
//创建一个高斯模糊滤镜
let filter = CIFilter(name: "CIGaussianBlur");
filter?.setValue(inputRadius, forKey:"inputRadius");
//设置滤镜参数
filter?.setValue(outputCIImg, forKey: "inputImage");
//获得滤镜效果的结果,注意!返回的还是一个CIImage
let outputCIImage = filter!.outputImage;
上面是一个简单的创建滤镜处理图像的过程,但是等一等,怎么把结果显示到屏幕上呢?
如果你查一下api,一定会发现UIImage有个类方法直接通过CIImage对象生成UIImage,问题解决了?但是推荐你不要这么干,原因等下解释,请用如下方式获得UIImage:
let eaglContext = EAGLContext(api: EAGLRenderingAPI.openGLES2);
let ciContext = CIContext(eaglContext: eaglContext!);
let cgImage = ciContext.createCGImage(outputCIImg!, from: (outputCIImg?.extent)!);
let outImg = UIImage(cgImage: cgImage!, scale: (inputImg?.scale)!, orientation: UIImageOrientation.up);
刚才介绍过的CIContext终于隆重登场了,来解释一下刚才的问题,为什么不直接用UIImage(CIImage)来获得UIImage?因为如果你用这样的方式,实际上程序会在内部创建一个CIContext对象来渲染CIImage。设想一下,如果你需要反复这样操作,会有什么后果。
这里另外一个问题就是CIContext的初始化了,实际上如果不填入EAGLContext对象,也是可以创建的let ciContext = CIContext()
但这样,实际上选择了用CPU渲染图像。如果想用GPU,请使用示例当中的创建方法。
滤镜和属性
Core Image的滤镜数量有127种,都是按名字创建的,如果不想查文档获得每个滤镜的名字,还有一个方法:
let filterNames = CIFilter.filterNamesInCategory(kCICategoryBuiltIn) as [String]
另外,如果想得到一个滤镜的输入和输出参数,我们就可以分别获取inputKeys和outputKeys数组。它们都返回NSString的数组。
人脸识别
CoreImage最好玩的地方,是支持人脸识别:
let iCIImg = CIImage(image: inputImg);
let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [CIDetectorAccuracy : CIDetectorAccuracyHigh]);
let resultArr = faceDetector?.features(in: iCIImg!);
//return resultArr as! [CIFaceFeature]?;
for f in resultArr!
{
if(f.hasLeftEyePosition)
{
print("left eye posion:", f.leftEyePosition);
}
if(f.hasRightEyePosition)
{
print("right eye posion:", f.rightEyePosition);
}
if(f.hasMouthPosition)
{
print("mouth posion:", f.mouthPosition)
}
}
输出结果:
left eye posion: (1360.046875, 1115.78125)
right eye posion: (1758.109375, 1251.484375)
mouth posion: (1547.015625, 880.5625)