【发布时间】:2012-02-12 07:58:43
【问题描述】:
我想在我的应用中使用 自定义 过滤器。现在我知道我需要使用 Core Image 框架,但我不确定这是正确的方法。 Core Image 框架用于 Mac OS 和 iOS 5.0 - 我不确定它是否可以用于自定义 CIFilter 效果。 你能帮我解决这个问题吗? 谢谢大家!
【问题讨论】:
标签: ios image-processing core-image
我想在我的应用中使用 自定义 过滤器。现在我知道我需要使用 Core Image 框架,但我不确定这是正确的方法。 Core Image 框架用于 Mac OS 和 iOS 5.0 - 我不确定它是否可以用于自定义 CIFilter 效果。 你能帮我解决这个问题吗? 谢谢大家!
【问题讨论】:
标签: ios image-processing core-image
过时
您还不能在 iOS 中创建自己的自定义内核/过滤器。具体见http://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/CoreImaging/ci_intro/ci_intro.html:
虽然此文档包含在参考库中,但它具有 未针对 iOS 5.0 进行详细更新。即将进行的修订将 详细介绍 iOS 上 Core Image 的差异。特别是,关键 不同之处在于 iOS 上的 Core Image 不包括 创建自定义图像过滤器。
(粗体字)
【讨论】:
CIKernel's)。
正如 Adam 所说,目前 iOS 上的 Core Image 不像旧的 Mac 实现那样支持自定义内核。这将您可以对框架执行的操作限制为现有过滤器的某种组合。
(更新:2012 年 2 月 13 日)
出于这个原因,我为 iOS 创建了一个名为 GPUImage 的开源框架,它允许您使用 OpenGL ES 2.0 片段着色器创建自定义过滤器以应用于图像和视频。我在my post on the topic 中描述了更多关于这个框架如何运作的信息。基本上,您可以提供自己的自定义 OpenGL 着色语言 (GLSL) 片段着色器来创建自定义过滤器,然后针对静态图像或实时视频运行该过滤器。该框架兼容所有支持 OpenGL ES 2.0 的 iOS 设备,并且可以创建面向 iOS 4.0 的应用程序。
例如,您可以使用如下代码设置实时视频过滤:
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];
作为定义过滤器的自定义片段着色器程序的示例,以下应用了棕褐色调效果:
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 开始,您可以为过滤器创建自定义内核。您可以在以下位置找到更多信息:
【讨论】:
您可以比 MacOS X 的 Image Unit 插件更轻松地为 iOS 创建自定义过滤器,即使 iOS 支持 Image Unit 插件,它们也会成为首选。问题是您实际上不能“打包”它们或以其他方式将它们捆绑为像 Image Unit 插件这样的资源;您必须向使用它们的开发人员公开您的源代码。而且,它们只对开发人员有用;您不能将它们分发给 iOS 图形应用程序的最终用户,就像对导入第三方 Core Image 过滤器的 MacOS X 图形应用程序一样。为此,您必须将它们嵌入到照片编辑扩展中。
不过,即使使用 iOS 的自定义 Core Image 过滤器处理图像也比使用 Image Unit 插件更容易。没有导入,接下来是配置 .plist 和描述文件等令人困惑的任务。
iOS 的自定义 Core Image 过滤器只是一个 Cocoa Touch 类,它是 CIFilter 的子类;在其中,您指定输入参数(始终至少是图像)、自定义属性设置及其默认值,然后是内置或自定义 Core Image 过滤器的任意组合。如果您想将 OpenGL 内核添加到图像处理管道,您只需添加一个 CIKernel 方法,该方法会将您编写的 .cikernel 加载到单独的文件中。
这种为 iOS 开发自定义核心图像过滤器的特殊方法的美妙之处在于,自定义过滤器的实例化和调用方式与内置过滤器相同:
CIFilter* PrewittKernel = [CIFilter filterWithName:@"PrewittKernel"];
CIImage *result = [CIFilter filterWithName:@"PrewittKernel" keysAndValues:kCIInputImageKey, self.inputImage, nil].outputImage;
这是一个使用 OpenGL 将 Prewitt 运算符应用于图像的简单示例;首先是 Cocoa Touch 类(继承 CIFilter),然后是 CIKernel 文件(包含 OpenGL ES 3.0 代码):
头文件:
//
// PrewittKernel.h
// Photo Filter
//
// Created by James Alan Bush on 5/23/15.
//
//
#import <CoreImage/CoreImage.h>
@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 <CoreImage/CoreImage.h>
@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(h.r, max(h.g, h.b));
float v_max = max(v.r, max(v.g, v.b));
float mag = length(vec2(h_max, v_max)) * 1.0;
return vec4(vec3(mag), 1.0);
}
这是另一个过滤器,它通过使用内置核心图像过滤器从原始图像中减去(或者更确切地说,区分)高斯模糊图像来生成不锐化的蒙版 - 没有核心图像内核代码 (OpenGL);它展示了如何指定和使用自定义属性,即高斯模糊的半径:
头文件:
//
// GaussianKernel.h
// Chroma
//
// Created by James Alan Bush on 7/12/15.
// Copyright © 2015 James Alan Bush. All rights reserved.
//
#import <CoreImage/CoreImage.h>
@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
【讨论】: