Core Image

概念

Core Image:一个OS X和iOS的图像处理框架,Core Image框架最早出现于iOS5,iOS6对这个框架进行了扩展。

滤镜:是一个对象,有很多输入和输出,并执行一些变换。例如,模糊滤镜可能需要输入图像和一个模糊半径来产生适当的模糊后的输出图像。

滤镜链:是一个链接在一起的滤镜网络,使得一个滤镜的输出可以是另一个滤镜的输入。当多个滤镜连接成一个滤镜链,Core Image便把滤镜内核串在一起来构建一个可在GPU上运行的高效程序。通常情况下,直到滤镜图表的最后一个滤镜的输出被请求之前都不会发生分配或处理。滤镜链相当于生成一个改进的滤镜,从而一次性的处理图像达到目标效果,而不是对同一个图像顺序地多次应用单个滤镜。为了完成工作,Core Image需要一个称为上下文的对象。

上下文(context):这个上下文是框架真正工作的地方,它需要分配必要的内存,并编译和运行滤镜内核来执行图像处理。在上下文创建的时候,我们需要给它设定为是基于GPU还是CPU。基于GPU的话,处理速度更快,因为利用了GPU硬件的并行优势。但是GPU受限于硬件纹理尺寸,而且如果你的程序在后台继续处理和保存图片的话,就需要使用CPU,因为当app切换到后台状态时GPU处理会被打断。建立一个上下文是非常昂贵的,所以会创建一个反复使用的上下文。

查询可用的滤镜样式

NSArray *filterNames = [CIFilter filterNamesInCategory:kCICategoryBuiltIn];

新建一个滤镜

CIFilter *vignetteFilter = [CIFilter filterWithName:@"CIVignette"];

设置滤镜参数,采用KVC方式

[vignetteFilter setValue:@(1.75) forKey:@"inputRadius"];
[vignetteFilter setValue:@(1.0) forKey:@"inputIntensity"];

也可以在新建滤镜时带入参数

CIFilter *vignetteFilter = [CIFilter filterWithName:@"CIVignette" withInputParameters:@{@"inputRadius":@(1.75), @"inputIntensity":@(1.0)}];

为了查看各种输入参数和输出参数,可以获取滤镜的inputKeys属性和outputKeys属性,并且,通过获取滤镜的attributes属性,可以查看每个输入参数的详细信息

NSArray *inputKeys = vignetteFilter.inputKeys;
NSArray *outputKeys = vignetteFilter.outputKeys;
NSDictionary *attr = vignetteFilter.attributes; 

vignetteFilter的inputKeys属性如下,可以看出除了inputImage,还有两个可以设置的参数


Core Image_第1张图片
inputKeys

vignetteFilter的outputKeys属性如下,仅含有outputImage


outputKeys

vignetteFilter的attributes属性如下,可以发现其中含有inputKeys中所有的对应项
Core Image_第2张图片
attr

其中inputRadius的展开如下,其中包含了该参数的数据类型、默认值、最大最小值等


Core Image_第3张图片
item of attr

创建上下文

// 基于CPU
CIContext *context = [CIContext contextWithOption:@{kCIContextUseSoftwareRenderer:@YES}];
// 基于GPU
CIContext *context = [CIContext contextWithOption:nil];
// 使用OpenGL提高性能
EAGLContext *eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
CIContext *context = [CIContext contextWithEAGLContext:eaglContext];

实战

  • UIImage和CIImage之间的转化
    将 UIImage 转化成 CIImage 需要借助中间的 CGImage
UIImage *uiImage = [UIImage imageNamed:@"dog.png"]; 
// 错误的方式,会得到nil
CIImage *ciImage1 = uiImage.CIImage;
// 正确的方式
CIImage *ciImage2 = [[CIImage alloc] initWithCGImage:uiImage.CGImage]; 

将 CIImage 转化成 UIImage 也需要借助中间的 CGImage
直接从一个 CIImage 创建 UIImage 也是可以的,但这种方法存在缺陷:如果你试图在一个 UIImageView 上显示这样的图像,其 contentMode 属性将被忽略;使用过渡的 CGImage 则需要一个额外的步骤,但可以修补这一缺陷

// 不建议使用的方式,生产默认的临时的上下文,具有缺陷
UIImage *image1 = [[UIImage alloc] initWithCIImage:ciImage];
// 建议使用的方式
UIImage *imagei2 = [self makeUIImageFromCIImage:ciImage];
// makeUIImageFromCIImage实现
 - (UIImage *)makeUIImageFromCIImage:(CIImage *)ciImage 
{   
EAGLContext *eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
CIContext *context = [CIContext contextWithEAGLContext:eaglContext];   
CGImageRef cgImage = [context createCGImage:ciImage fromRect:[ciImage extent]];
UIImage* uiImage = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);   
return uiImage;
}

上面代码中,每次调用makeUIImageFromCIImage方法都会生成一个临时的上下文(CIContext),开销很大。可以保存为当前类的属性,进行复用

_glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 
if (!_glContext) { 
NSLog(@"Failed to create ES context"); 
} 
_context = [CIContext contextWithEAGLContext:_glContext];    
  • 自己定制一个滤镜链
 - (UIImage *)makeFilter:(UIImage *)originImage
{
// 根据传入的UIImage获得CIImage
CIImage *inputImage = [[CIImage alloc] initWithCGImage:originImage.CGImage]; 
// 第一个滤镜,黑白滤镜
CIColor *sepiaColor = [CIColor colorWithRed:0.76 green:0.65 blue:0.54];  
CIFilter *monochromeFilter = [CIFilter filterWithName:@"CIColorMonochrome" withInputParameters:@{@"inpu Color":sepiaColor, @"inputIntensity":@(1.0)}];  
[monochromeFilter setValue:inputImage forKey:@"inputImage"];
// 第二个滤镜,暗角滤镜   
CIFilter *vignetteFilter = [CIFilter filterWithName:@"CIVignette" withInputParameters:@{@"inputRadius":@(1.75), @"inputIntensity":@(1.0)}];  
[vignetteFilter setValue:monochromeFilter.outputImage forKey:@"inputImage"];
// 返回最后一个滤镜输出的CIImage转化成的UIImage    
return [self makeUIImageFromCIImage:vignetteFilter.outputImage];
}

效果图如下


Core Image_第4张图片
滤镜处理前

Core Image_第5张图片
滤镜处理后

人脸识别

判断图片中有没有人脸

 - (BOOL)hasFace:(UIImage *)image
{  
NSArray *features = [self featuresWithImage:image];
return !!features.count;
}
 - (NSArray *)featuresWithImage:(UIImage *)image
{
CIDetector *faceDetector = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];    
CIImage *ciimg = [CIImage imageWithCGImage:image.CGImage];
NSArray *features = [faceDetector featuresInImage:ciimg];
return features;
}

获取图片中所有左眼位置、右眼位置以及嘴部位置

// 获取图像中所有的左眼位置
// (hasLeftEyePosition, leftEyePosition) : (特征中是否有左眼位置, 左眼位置坐标)
// (hasRightEyePosition, rightEyePosition) : (特征中是否有右眼位置, 右眼位置坐标)
// (hasMouthPosition, mouthPosition) : (特征中是否有嘴部位置, 嘴部位置坐标)
 - (NSArray *)leftEyePositionsWithImage:(UIImage *)image 
{   
if (![self hasFace:image]) 
return nil; 
NSArray *features = [self featuresWithImage:image];    
NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:features.count];   
for (CIFaceFeature *f in features) {         
if (f.hasLeftEyePosition) [arrM addObject:[NSValue valueWithCGPoint:f.leftEyePosition]];   
}   
return arrM; 
}

CIFaceFeature中还提供了是否有微笑、左右闭眼等。

附录

图像滤镜完整列表及用法示例
Core Image官方文档
对Core Image进行封装
Core Image与视频处理

你可能感兴趣的:(Core Image)