如何使用Core Image Framework为iOS创建简单的自定义filter?

我想在我的应用程序中使用自定义filter。 现在我知道我需要使用Core Image框架,但我不确定这是正确的方法。 Core Image框架用于Mac OSiOS 5.0 – 我不确定它是否可用于自定义CIFilter效果。 你能帮我解决这个问题吗? 谢谢大家!

过时的

您还无法在iOS中创建自己的自定义内核/filter。 请参阅http://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/CoreImaging/ci_intro/ci_intro.html ,具体如下:

尽管此文档包含在参考库中,但尚未针对iOS 5.0进行详细更新。 即将发布的修订版将详细介绍iOS上Core Image的差异。 特别是,关键的区别在于iOS上的Core Image不包括创建自定义图像filter的function

(Bolding矿)

正如亚当所说,目前iOS上的Core Image不支持像旧版Mac实现那样的自定义内核。 这限制了您可以使用框架做某种现有filter的组合。

(更新:2012年2月13日)

出于这个原因,我为iOS创建了一个名为GPUImage的开源框架,它允许您使用OpenGL ES 2.0片段着色器创建应用于图像和video的自定义filter。 我在关于该主题的post中详细描述了该框架的运作方式。 基本上,您可以提供自己的自定义OpenGL着色语言(GLSL)片段着色器来创建自定义filter,然后针对静态图像或实时video运行该filter。 此框架与支持OpenGL ES 2.0的所有iOS设备兼容,并且可以创建面向iOS 4.0的应用程序。

例如,您可以使用以下代码设置实时video的过滤:

 GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack]; GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"]; GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, viewWidth, viewHeight)]; // Add the view somewhere so it's visible [videoCamera addTarget:thresholdFilter]; [customFilter addTarget:filteredVideoView]; [videoCamera startCameraCapture]; 

作为定义filter的自定义片段着色器程序的示例,以下应用棕褐色调效果:

 varying highp vec2 textureCoordinate; uniform sampler2D inputImageTexture; void main() { lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate); lowp vec4 outputColor; outputColor.r = (textureColor.r * 0.393) + (textureColor.g * 0.769) + (textureColor.b * 0.189); outputColor.g = (textureColor.r * 0.349) + (textureColor.g * 0.686) + (textureColor.b * 0.168); outputColor.b = (textureColor.r * 0.272) + (textureColor.g * 0.534) + (textureColor.b * 0.131); gl_FragColor = outputColor; } 

用于在Mac上编写自定义Core Image内核的语言与GLSL非常相似。 事实上,你可以做一些你不能在桌面Core Image中做的事情,因为Core Image的内核语言缺少一些GLSL所拥有的东西(比如分支)。

原始接受的答案已折旧。 从iOS 8开始,您可以为filter创建自定义内核。 您可以在以下位置找到更多相关信息:

  • WWDC 2014年会议514核心形象的进展
  • 第514节会议记录
  • WWDC 2014 Session 515为iOS开发核心图像filter
  • 第515节会议记录

您可以比MacOS X的Image Unit插件更容易为iOS创建自定义filter,以便它们成为首选,即使iOS支持Image Unit插件也是如此。 问题是你实际上无法“打包”它们或以其他方式将它们捆绑为像Image Unit插件这样的资源; 您必须将源代码公开给使用它们的开发人员。 而且,它们只对开发人员有用; 您无法像导入第三方Core Imagefilter的MacOS X图形应用程序那样将它们分发给iOS图形应用程序的最终用户。 为此,您必须将它们嵌入照片编辑扩展中。

尽管如此,使用iOS自定义Core Imagefilter处理图像仍然比使用Image Unit插件更容易。 没有导入,然后是配置.plist和描述文件以及什么不是的令人困惑的任务。

iOS的自定义Core Imagefilter只是一个Cocoa Touch类,它是CIFilter的子类; 在其中,您指定输入参数(始终至少是图像),自定义属性设置及其默认值,然后指定内置或自定义Core Imagefilter的任意组合。 如果要将OpenGL内核添加到图像处理管道,只需添加一个CIKernel方法,该方法将您编写的.cikernel加载到单独的文件中。

开发iOS自定义核心图像filter的这种特殊方法的优点是自定义filter的实例化和调用方式与内置filter相同:

 CIFilter* PrewittKernel = [CIFilter filterWithName:@"PrewittKernel"]; CIImage *result = [CIFilter filterWithName:@"PrewittKernel" keysAndValues:kCIInputImageKey, self.inputImage, nil].outputImage; 

这是一个使用OpenGL将Prewitt运算符应用于图像的简单示例; 首先,Cocoa Touch Class(inheritanceCIFilter),然后是CIKernel文件(包含OpenGL ES 3.0代码):

头文件:

 // // PrewittKernel.h // Photo Filter // // Created by James Alan Bush on 5/23/15. // // #import  @interface PrewittKernel : CIFilter { CIImage *inputImage; } @property (retain, nonatomic) CIImage *inputImage; @end 

实施文件:

 // // PrewittKernel.m // Photo Filter // // Created by James Alan Bush on 5/23/15. // // #import  @interface PrewittKernel : CIFilter { CIImage *inputImage; } @property (retain, nonatomic) CIImage *inputImage; @end @implementation PrewittKernel @synthesize inputImage; - (CIKernel *)prewittKernel { static CIKernel *kernelPrewitt = nil; NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(@"PrewittKernel")]; NSStringEncoding encoding = NSUTF8StringEncoding; NSError *error = nil; NSString *code = [NSString stringWithContentsOfFile:[bundle pathForResource:@"PrewittKernel" ofType:@"cikernel"] encoding:encoding error:&error]; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ kernelPrewitt = [CIKernel kernelWithString:code]; }); return kernelPrewitt; } - (CIImage *)outputImage { CIImage *result = self.inputImage; return [[self prewittKernel] applyWithExtent:result.extent roiCallback:^CGRect(int index, CGRect rect) { return CGRectMake(0, 0, CGRectGetWidth(result.extent), CGRectGetHeight(result.extent)); } arguments:@[result]]; } @end 

CIKernel(OpenGL ES 3.0):

 /* PrewittKernel.cikernel */ kernel vec4 prewittKernel(sampler image) { vec2 xy = destCoord(); vec4 bottomLeftIntensity = sample(image, samplerTransform(image, xy + vec2(-1, -1))); vec4 topRightIntensity = sample(image, samplerTransform(image, xy + vec2(+1, +1))); vec4 topLeftIntensity = sample(image, samplerTransform(image, xy + vec2(+1, -1))); vec4 bottomRightIntensity = sample(image, samplerTransform(image, xy + vec2(-1, +1))); vec4 leftIntensity = sample(image, samplerTransform(image, xy + vec2(-1, 0))); vec4 rightIntensity = sample(image, samplerTransform(image, xy + vec2(+1, 0))); vec4 bottomIntensity = sample(image, samplerTransform(image, xy + vec2(0, -1))); vec4 topIntensity = sample(image, samplerTransform(image, xy + vec2(0, +1))); vec4 h = vec4(-topLeftIntensity - topIntensity - topRightIntensity + bottomLeftIntensity + bottomIntensity + bottomRightIntensity); vec4 v = vec4(-bottomLeftIntensity - leftIntensity - topLeftIntensity + bottomRightIntensity + rightIntensity + topRightIntensity); float h_max = max(hr, max(hg, hb)); float v_max = max(vr, max(vg, vb)); float mag = length(vec2(h_max, v_max)) * 1.0; return vec4(vec3(mag), 1.0); } 

这是另一个filter,它通过使用内置的核心图像filter(无核心图像内核代码(OpenGL))从原始图像中减去(或者更确切地说,差异)高斯模糊图像来生成非锐化蒙版; 它显示了如何指定和使用自定义属性,即高斯模糊的半径:

头文件:

 // // GaussianKernel.h // Chroma // // Created by James Alan Bush on 7/12/15. // Copyright © 2015 James Alan Bush. All rights reserved. // #import  @interface GaussianKernel : CIFilter { CIImage *inputImage; NSNumber *inputRadius; } @property (retain, nonatomic) CIImage *inputImage; @property (retain, nonatomic) NSNumber *inputRadius; @end 

实施文件:

 // // GaussianKernel.m // Chroma // // Created by James Alan Bush on 7/12/15. // Copyright © 2015 James Alan Bush. All rights reserved. // #import "GaussianKernel.h" @implementation GaussianKernel @synthesize inputImage; @synthesize inputRadius; + (NSDictionary *)customAttributes { return @{ @"inputRadius" : @{ kCIAttributeMin : @3.0, kCIAttributeMax : @15.0, kCIAttributeDefault : @7.5, kCIAttributeType : kCIAttributeTypeScalar } }; } - (void)setDefaults { self.inputRadius = @7.5; } - (CIImage *)outputImage { CIImage *result = self.inputImage; CGRect rect = [[GlobalCIImage sharedSingleton].ciImage extent]; rect.origin = CGPointZero; CGRect cropRectLeft = CGRectMake(0, 0, rect.size.width, rect.size.height); CIVector *cropRect = [CIVector vectorWithX:rect.origin.x Y:rect.origin.y Z:rect.size.width W:rect.size.height]; result = [[CIFilter filterWithName:@"CIGaussianBlur" keysAndValues:kCIInputImageKey, result, @"inputRadius", [NSNumber numberWithFloat:inputRadius.floatValue], nil].outputImage imageByCroppingToRect:cropRectLeft]; result = [CIFilter filterWithName:@"CICrop" keysAndValues:@"inputImage", result, @"inputRectangle", cropRect, nil].outputImage; result = [CIFilter filterWithName:@"CIDifferenceBlendMode" keysAndValues:kCIInputImageKey, result, kCIInputBackgroundImageKey, result, nil].outputImage; return result; } @end