【问题标题】:How to create simple custom filter for iOS using Core Image Framework?如何使用 Core Image Framework 为 iOS 创建简单的自定义过滤器?
【发布时间】:2012-02-12 07:58:43
【问题描述】:

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

【问题讨论】:

    标签: ios image-processing core-image


    【解决方案1】:

    过时

    您还不能在 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 不包括 创建自定义图像过滤器

    (粗体字)

    【讨论】:

    • 请注意,虽然不能自己编写内核,但也许可以将现有的CIFilters组合起来得到你想要的效果。你想创造什么效果?
    • 确实如此。您也可以完全自己编写自己的图像过滤器函数,直接对数据进行操作。如果您明智地使用 Accelerate 框架,您还将获得重要的硬件支持。
    • iOS 8 添加了自定义 CoreImage 过滤器(即自定义 CIKernel's)。
    【解决方案2】:

    正如 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 所具备的一些东西(比如分支)。

    【讨论】:

    • 感谢您的回答!是的,如果使用 openGL,这对我来说是最好的结果。但是现在我对openGL中的这个特性有了一点了解
    【解决方案3】:

    原始接受的答案已贬值。从 iOS 8 开始,您可以为过滤器创建自定义内核。您可以在以下位置找到更多信息:

    【讨论】:

    • 感谢您的更新,这是一个老问题,我认为如果其他人使用 iOS 8 sdk,新答案可以帮助他们找出一些新的细节。
    【解决方案4】:

    您可以比 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
    

    【讨论】:

      猜你喜欢
      • 2012-05-27
      • 2018-05-15
      • 1970-01-01
      • 2019-04-04
      • 2018-05-21
      • 1970-01-01
      • 1970-01-01
      • 2014-05-19
      • 1970-01-01
      相关资源
      最近更新 更多