How to create simple custom filter for iOS using Core Image Framework?

后端 未结 4 1828
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-02-06 13:33

I want to use in my app an custom filter. Now I know that I need to use Core Image framework, but i not sure that is right way. Core Image fram

4条回答
  •  醉酒成梦
    2021-02-06 14:14

    You can create custom filters for iOS easier than an Image Unit plug-in for MacOS X, so much so that they would be preferred, even if Image Unit plug-ins were supported by iOS. The problem is you cannot actually "package" them or otherwise bundle them as a resource like Image Unit plug-ins; you have to expose your source code to developers that use them. Moreover, they are only useful to developers; you cannot distribute them to end-users of iOS graphics apps the same way you can for MacOS X graphics apps that import third-party Core Image filters. For that, you must embed them in a Photo Editing Extension.

    Still, even processing images with a custom Core Image filter for iOS is easier than with an Image Unit plug-in. There's no importing, followed by the confusing task of configuring .plist and description files and what-not.

    A custom Core Image filter for iOS is simply a Cocoa Touch Class that is a subclass of CIFilter; in it, you specify input parameters (always at least the image), custom attributes settings and their defaults, and then any combination of built-in or custom Core Image filters. If you want to add an OpenGL kernel to the image-processing pipeline, you simply add a CIKernel method, which loads the .cikernel you write in a separate file.

    The beauty of this particular method for developing a custom Core Image Filter for iOS is that custom filters are instantiated and called the same way as built-in filters:

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

    Here's a simple example that uses OpenGL to apply the Prewitt Operator to an image; first, the Cocoa Touch Class (subclassing CIFilter), then, the CIKernel file (containing the OpenGL ES 3.0 code):

    The header file:

    //
    //  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
    

    The implementation file:

    //
    //  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
    

    The 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);
    }
    

    Here's another filter that generates an unsharp mask by subtracting (or, rather, differencing) a Gaussian blurred image from the original using built-in Core Image filters—no Core Image kernel code (OpenGL); it shows how to specify and use a custom attribute, namely, the radius of the Gaussian blur:

    The header file:

    //
    //  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
    

    The implementation file:

    //
    //  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
    

提交回复
热议问题