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