Пользовательские фильтры для iOS можно создавать проще, чем плагин Image Unit для MacOS X, настолько, что они будут предпочтительнее, даже если плагины Image Unit поддерживаются iOS.Проблема в том, что вы не можете «упаковать» их или иным образом связать их как ресурс, такой как подключаемые модули Image Unit;Вы должны предоставить свой исходный код разработчикам, которые их используют.Более того, они полезны только для разработчиков;вы не можете распространять их среди конечных пользователей графических приложений iOS так же, как и для графических приложений MacOS X, которые импортируют сторонние фильтры Core Image.Для этого вы должны встроить их в расширение для редактирования фотографий.
Тем не менее даже обработка изображений с помощью пользовательского фильтра Core Image для iOS проще, чем с помощью подключаемого модуля Image Unit.Нет импорта, после чего следует запутанная задача настройки .plist и файлов описания и чего-то еще.
Пользовательский фильтр Core Image для iOS - это просто Cocoa Touch Class, который является подклассом 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 к изображению;сначала класс Touch Cocoa (подкласс 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);
}
Вот еще один фильтр, который генерирует нечеткую маску путем вычитания (или, скорее, разности) размытого изображения Гаусса из оригинала с использованием встроенного- в фильтрах Core Image - нет кода ядра Core Image (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