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

时间:2012-01-17 14:03:00

标签: ios image-processing core-image

我想在我的应用中使用自定义过滤器。现在我知道我需要使用核心图像框架,但我不确定这是正确的方法。 Core Image框架用于 Mac OS iOS 5.0 - 我不确定它是否可用于自定义CIFilter效果。 你能帮我解决这个问题吗? 谢谢大家!

4 个答案:

答案 0 :(得分:21)

正如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的内核语言缺少GLSL的一些东西(比如分支)。

答案 1 :(得分:9)

原始接受的答案已弃用。从iOS 8开始,您可以为过滤器创建自定义内核。您可以在以下网址找到更多相关信息:

答案 2 :(得分:5)

<强>过时

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

  

虽然此文档包含在参考库中,但它具有   iOS 5.0未详细更新。即将进行的修订将会   详细介绍iOS上Core Image的差异。特别是关键   差异在于iOS上的核心图像不具备此功能   创建自定义图像过滤器

(Bolding mine)

答案 3 :(得分:0)

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

尽管如此,使用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 Class(继承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