Создание пользовательских фильтров
На OS X, если фильтры, предоставленные Базовым Изображением, не обеспечивают функциональность, Вам нужно, можно записать собственный фильтр. (Пользовательские фильтры не доступны на iOS.) Можно включать фильтр как часть проекта приложения, или можно упаковать один или несколько фильтров как автономный модуль изображения. Модули изображения используют NSBundle
класс и представляет сменную архитектуру для фильтров.
Следующие разделы предоставляют подробную информацию о том, как создать и использовать пользовательские фильтры и модули изображения:
Выражение операций обработки изображений в базовом изображении
Создание Пользовательского Фильтра описывает методы, которые необходимо реализовать и другие требования фильтра.
Используя Ваш Собственный Фильтр говорит то, что является потребностью в Вас использовать фильтр в Вашем собственном приложении. (Если Вы хотите упаковать его как автономный модуль изображения, посмотрите Упаковку и Загрузку Модулей Изображения.)
Когда необходимо предоставить метод для вычисления этой области, предоставление Функции ROI предоставляет информацию об области интереса и. (Это не всегда необходимо.)
Запись Неисполнимых Фильтров является обязательным для чтения разделом для любого, кто планирует записать фильтр, который является неисполнимой программой CPU, поскольку это перечисляет требования для таких фильтров. Модуль изображения может содержать оба вида фильтров. Неисполнимые фильтры CPU безопасны, потому что они не могут предоставить кров вирусам и Троянским коням. Отфильтруйте клиенты, которые являются сознательной безопасностью, может хотеть использовать только те фильтры, которые являются неисполнимой программой CPU.
Примеры Подпрограммы ядра обеспечивают подпрограммы ядра для трех демонстрационных фильтров: прояснение, умножьтесь, и искажение дыры.
Выражение операций обработки изображений в базовом изображении
Базовое Изображение работает таким образом, что ядро (т.е. подпрограмма обработки на пиксель) записано как вычисление, где выходной пиксель выражен с помощью обратного отображения назад для соответствующих пикселей входных изображений ядра. Несмотря на то, что можно выразить большинство пиксельных вычислений этот путь — некоторые более естественно, чем другие — существуют некоторые операции обработки изображений, для которых это трудно, если не невозможный. Перед записью фильтра можно хотеть рассмотреть, может ли работа обработки изображений быть выражена в Базовом Изображении. Например, вычисления гистограммы трудно описать как обратное отображение к исходному изображению.
Создание пользовательского фильтра
Этот раздел показывает, как создать Базовый фильтр Изображения, имеющий часть Objective C и часть ядра. Путем выполнения шагов в этом разделе Вы создадите фильтр, который является исполнимой программой CPU. Если Вы хотели бы как модуль изображения путем следования инструкциям в Упаковке и Загрузке Модулей Изображения, можно упаковать этот фильтр, вместе с другими фильтрами. Или, можно просто использовать фильтр из собственного приложения. Посмотрите Используя Свой Собственный Фильтр для подробных данных.
Фильтр в этом разделе предполагает, что совпадают область интереса (ROI) и домен определения. Если Вы хотите записать фильтр, для которого это предположение не является истиной, удостоверьтесь, что Вы также читаете Предоставление Функции ROI. Перед созданием собственного фильтра удостоверьтесь, что Вы понимаете Базовые координатные пространства Изображения. Посмотрите Создавание Словаря Фильтров.
Для создания пользовательского исполнимого фильтра CPU выполните следующие шаги:
Каждый шаг описан подробно в разделах, следующих за использованием фильтра удаления тумана как пример. Эффект фильтра удаления тумана состоит в том, чтобы скорректировать яркость и контраст изображения, и применять увеличение резкости к нему. Этот фильтр полезен для исправления изображений, взятых через легкую вуаль или туман, обычно имеющий место при взятии изображения от самолета. Рисунок 9-1 показывает изображение прежде и после обработки с фильтром удаления тумана. Приложение с помощью фильтра обеспечивает ползунки, позволяющие пользователю скорректировать входные параметры фильтра.
Запишите код ядра
Код, выполняющий обработку на пиксель, находится в файле с .cikernel
расширение. Можно включать больше чем одну подпрограмму ядра в этот файл. Если Вы хотите сделать свой код модульным, можно также включать другие подпрограммы. Вы указываете ядро с помощью подмножества Языка Штриховки OpenGL (glslang) и Базовых расширений Изображения его. Посмотрите Базовую Ссылку Языка Ядра Изображения для получения информации о допустимых элементах языка.
Подпись подпрограммы ядра должна возвратить вектор (vec4
) это содержит результат отображения исходного пикселя к целевому пикселю. Базовое Изображение вызывает подпрограмму ядра один раз для каждого пикселя. Следует иметь в виду, что Ваш код не может накопить знание от пикселя до пикселя. Хорошая стратегия при записи кода состоит в том, чтобы переместить как можно больше инвариантного вычисления от фактического ядра и поместить его в часть Objective C фильтра.
Перечисление 9-1 показывает подпрограмму ядра для фильтра удаления тумана. Подробное объяснение каждой пронумерованной строки кода следует за перечислением. (Существуют примеры других обрабатывающих пиксель подпрограмм в Примерах Подпрограммы Ядра и в Учебном руководстве по Модулю Изображения.)
Перечисление 9-1 подпрограмма ядра для фильтра удаления тумана
kernel vec4 myHazeRemovalKernel(sampler src, // 1 |
__color color, |
float distance, |
float slope) |
{ |
vec4 t; |
float d; |
d = destCoord().y * slope + distance; // 2 |
t = unpremultiply(sample(src, samplerCoord(src))); // 3 |
t = (t - d*color) / (1.0-d); // 4 |
return premultiply(t); // 5 |
} |
Вот то, что делает код:
Берет четыре входных параметра и возвращает вектор. Когда Вы объявляете интерфейс для фильтра, необходимо удостовериться, что объявили то же число входных параметров, как Вы указываете в ядре. Ядро должно возвратить a
vec4
тип данных.Вычисляет значение на основе y-значения координаты назначения и наклона и входных параметров расстояния.
destCoord
подпрограмма (предоставленный Базовым Изображением) возвращает позицию, в координатах рабочей области, пикселя, в настоящее время будучи вычисленным.Получает пиксельное значение, в пространстве сэмплера, сэмплера
src
это связано с пикселем текущей производительности после того, как любая матрица преобразования связалась сsrc
применяется. Вспомните, что Базовое Изображение использует компоненты цвета с предварительно умноженными альфа-значениями. Перед обработкой Вам нужны к unpremultiply значения цвета, которые Вы получаете от сэмплера.Вычисляет выходной вектор путем применения формулы удаления тумана, включающей наклон и вычисления расстояния и приводящей в соответствие с цветом.
Возвраты a
vec4
вектор, как требуется. Ядро выполняет работу умножения в обратном порядке прежде, чем возвратить результат, потому что Базовое Изображение использует компоненты цвета с предварительно умноженными альфа-значениями.
Используйте кварцевого композитора для тестирования подпрограммы ядра
Кварцевый Композитор является простым в использовании средством разработки, которое можно использовать для тестирования подпрограмм ядра.
Откройте Xcode.
Выберите Xcode> Open Developer Tool> More Developer Tools...
Выбор этого элемента возьмет Вас к developer.apple.com.
Регистрируйтесь к developer.apple.com.
Необходимо тогда видеть Загрузки для веб-страницы Разработчиков Apple.
Загрузите Графические Инструменты для пакета XCode, содержащего Кварцевого Композитора.
Кварцевый Композитор обеспечивает патч — Базовый Фильтр Изображения — в который можно поместить подпрограмму ядра. Вы просто открываете Inspector для Базового патча Фильтра Изображения, и или вставляете или вводите Ваш код в текстовое поле, как показано на рисунке 9-2.
После ввода кода входные порты патча автоматически создаются согласно прототипу функции ядра, как Вы видите на рисунке 9-3. Патч всегда имеет единственный выходной порт, представляющий получающееся изображение, произведенное ядром.
Простой состав, показанный на рисунке 9-3, импортирует файл образа с помощью патча Средства импорта Изображения, обрабатывает его через ядро, затем представляет результат на экране с помощью патча Billboard. Ваше ядро может использовать больше чем одно изображение или, если это генерирует вывод, это не могло бы потребовать никаких входных изображений.
Состав, который Вы создаете для тестирования ядра, может быть более сложным, чем показанный на рисунке 9-3. Например, Вы могли бы хотеть объединить свою подпрограмму ядра в цепочку с другими встроенными Базовыми фильтрами Изображения или с другими подпрограммами ядра. Кварцевый Композитор обеспечивает много других патчей, которые можно использовать в ходе тестирования подпрограммы ядра.
Объявите интерфейс для фильтра
.h
файл для фильтра содержит интерфейс, указывающий вводы фильтра, как показано в Перечислении 9-2. Ядро удаления тумана имеет четыре входных параметра: источник, цвет, расстояние и наклон. Интерфейс для фильтра должен также содержать эти входные параметры. Входные параметры должны быть в том же порядке, как указано для фильтра, и типы данных должны быть совместимыми между двумя.
Код перечисления 9-2, объявляющий интерфейс для фильтра удаления тумана
@interface MyHazeFilter: CIFilter |
{ |
CIImage *inputImage; |
CIColor *inputColor; |
NSNumber *inputDistance; |
NSNumber *inputSlope; |
} |
@end |
Запишите метод Init для объекта CIKernel
Файл реализации для фильтра содержит метод, инициализирующий Базовый объект ядра Изображения (CIKernel
) с подпрограммой ядра, указанной в .cikernel
файл. A .cikernel
файл может содержать больше чем одну подпрограмму ядра. Подробное объяснение каждой пронумерованной строки кода появляется после перечисления.
Перечисление 9-3 init метод, инициализирующий ядро
static CIKernel *hazeRemovalKernel = nil; |
- (id)init |
{ |
if(hazeRemovalKernel == nil) // 1 |
{ |
NSBundle *bundle = [NSBundle bundleForClass: [self class]]; // 2 |
NSString *code = [NSString stringWithContentsOfFile: [bundle |
pathForResource: @"MyHazeRemoval" |
ofType: @"cikernel"]]; // 3 |
NSArray *kernels = [CIKernel kernelsWithString: code]; // 4 |
hazeRemovalKernel = kernels[0]; // 5 |
} |
return [super init]; |
} |
Вот то, что делает код:
Проверки, ли
CIKernel
объект уже инициализируется.Возвращает пакет, динамично загружающийся
CIFilter
класс.Возвращает строку, создаваемую из имени файла в указанном пути, который в этом случае является
MyHazeRemoval.cikernel
файл.Создает a
CIKernel
объект от строки, указаннойcode
параметр. Каждая подпрограмма в.cikernel
файл, отмеченный как ядро, возвращается вkernels
массив. Этот пример имеет только одно ядро в.cikernel
файл, таким образом, массив содержит только один элемент.Наборы
hazeRemovalKernel
к первому ядру вkernels
массив. Если.cikernel
файл содержит больше чем одно ядро, Вы также инициализировали бы те ядра в этой подпрограмме.
Запишите метод пользовательских атрибутов
A customAttributes
метод позволяет клиентам фильтра получать атрибуты фильтра, такие как входные параметры, значения по умолчанию и минимальные и максимальные значения. (См. Ссылку класса CIFilter для полного списка атрибутов.) Фильтр не требуется, чтобы предоставлять любую информацию об атрибуте кроме его класса, но фильтр должен вести себя разумным способом, если не присутствуют атрибуты.
Как правило, это атрибуты что Ваш customAttributes
метод возвратился бы:
Параметры ввода и вывода
Класс атрибута для каждого параметра Вы предоставляете (обязательный)
Минимум, максимум и значения по умолчанию для каждого (дополнительного) параметра
Другая информация, столь же надлежащая, такая как ползунок минимальные и (дополнительные) максимальные значения
Перечисление 9-4 показывает customAttributes
метод для фильтра Тумана. Входные параметры inputDistance
и inputSlope
у каждого есть минимум, максимум, минимум ползунка, максимум ползунка, значение по умолчанию и набор значений идентификационных данных. Ползунок минимальные и максимальные значения используется для установки ползунков, показанных на рисунке 9-1. inputColor
параметру установили значение по умолчанию.
Перечисление 9-4 customAttributes
метод для фильтра Тумана
- (NSDictionary *)customAttributes |
{ |
return @{ |
@"inputDistance" : @{ |
kCIAttributeMin : @0.0, |
kCIAttributeMax : @1.0, |
kCIAttributeSliderMin : @0.0, |
kCIAttributeSliderMax : @0.7, |
kCIAttributeDefault : @0.2, |
kCIAttributeIdentity : @0.0, |
kCIAttributeType : kCIAttributeTypeScalar |
}, |
@"inputSlope" : @{ |
kCIAttributeSliderMin : @-0.01, |
kCIAttributeSliderMax : @0.01, |
kCIAttributeDefault : @0.00, |
kCIAttributeIdentity : @0.00, |
kCIAttributeType : kCIAttributeTypeScalar |
}, |
kCIInputColorKey : @{ |
kCIAttributeDefault : [CIColor colorWithRed:1.0 |
green:1.0 |
blue:1.0 |
alpha:1.0] |
}, |
}; |
} |
Запишите выходной метод изображения
outputImage
метод создает a CISampler
объект для каждого входного изображения (или маска изображения), создает a CIFilterShape
объект (если надлежащий), и применяет метод ядра. Перечисление 9-5 показывает outputImage
метод для фильтра удаления тумана. Первая вещь, которую делает код, состоит в том, чтобы установить сэмплер для выборки пикселей от входного изображения. Поскольку этот фильтр использует только одно входное изображение, кодовые наборы только один сэмплер.
Код вызывает apply:arguments:options:
метод CIFilter
произвести a CIImage
объект. Первый параметр к применять методу CIKernel
объект, содержащий функцию ядра удаления тумана. (См. Запись Код Ядра.) Вспоминают, что функция ядра удаления тумана берет четыре параметра: сэмплер, цвет, расстояние и наклон. Эти параметры передаются как следующие четыре параметра apply:arguments:options:
метод в Перечислении 9-5. Остающиеся параметры применять методу указывают опции (пары ключ/значение), управляющие, как Базовое Изображение должно оценить функцию. Можно передать один из трех ключей: kCIApplyOptionExtent
, kCIApplyOptionDefinition
, или kCIApplyOptionUserInfo
. Этот пример использует kCIApplyOptionDefinition
ключ для указания домена определения (DOD) выходного изображения. См. Ссылку класса CIFilter для описания этих ключей и для получения дополнительной информации об использовании apply:arguments:options:
метод.
Заключительный параметр nil
, указывает конец списка опций.
Перечисление 9-5 метод, возвращающий вывод изображения из фильтра удаления тумана
- (CIImage *)outputImage |
{ |
CISampler *src = [CISampler samplerWithImage: inputImage]; |
return [self apply: hazeRemovalKernel, src, inputColor, inputDistance, |
inputSlope, kCIApplyOptionDefinition, [src definition], nil]; |
} |
Перечисление 9-5 является простым примером. Реализация для Вашего outputImage
метод должен быть адаптирован в соответствии с Вашим фильтром. Если бы Ваш фильтр требует инвариантных циклом вычислений, Вы включали бы их в outputImage
метод, а не в ядре.
Зарегистрируйте фильтр
Идеально, Вы упакуете фильтр как модуль изображения, независимо от того, планируете ли Вы распределить фильтр другим или использовать его только в Вашем собственном приложении. Если Вы запланируете упаковать этот фильтр как модуль изображения, то Вы зарегистрируете свой фильтр с помощью CIPlugInRegistration
протокол описан в Упаковке и Загрузке Модулей Изображения. Можно пропустить остальную часть этого раздела.
Если по некоторым причинам Вы не захотите упаковывать фильтр как модуль изображения (который не рекомендуется), то необходимо будет зарегистрировать фильтр с помощью регистрационного метода CIFilter
класс описал показанный в Перечислении 9-6. Инициализировать вызовы метода registerFilterName:constructor:classAttributes:
. Необходимо зарегистрировать только имя дисплея (kCIAttributeFilterDisplayName
) и категории фильтра (kCIAttributeFilterCategories
). Все другие атрибуты фильтров должны быть указаны в customAttributes
метод. (См. Запись Метод Пользовательских атрибутов).
Имя фильтра является строкой для создания фильтра удаления тумана, когда Вы хотите использовать его. Объект конструктора указал реализации filterWithName:
метод (см. Запись Метод для Создания Экземпляров Фильтра). Атрибуты класса фильтра указаны как NSDictionary
объект. Имя дисплея — что Вы показали бы в пользовательском интерфейсе — для этого фильтра, является Съемником Тумана.
Перечисление 9-6 , Регистрирующее фильтр, который не является частью модуля изображения
+ (void)initialize |
{ |
[CIFilter registerFilterName: @"MyHazeRemover" |
constructor: self |
classAttributes: |
@{kCIAttributeFilterDisplayName : @"Haze Remover", |
kCIAttributeFilterCategories : @[ |
kCICategoryColorAdjustment, kCICategoryVideo, |
kCICategoryStillImage, kCICategoryInterlaced, |
kCICategoryNonSquarePixels]} |
]; |
} |
Запишите метод для создания экземпляров фильтра
Если Вы запланируете использовать этот фильтр только в Вашем собственном приложении, то необходимо будет реализовать a filterWithName:
метод, как описано в этом разделе. Если Вы планируете упаковать этот фильтр как модуль изображения для использования сторонними разработчиками, то можно пропустить этот раздел, потому что упакованные фильтры могут использовать filterWithName:
метод, предоставленный CIFilter
класс.
filterWithName:
метод, показанный в Перечислении 9-7, создает экземпляры фильтра, когда их требуют.
Перечисление 9-7 метод, создающий экземпляр фильтра
+ (CIFilter *)filterWithName: (NSString *)name |
{ |
CIFilter *filter; |
filter = [[self alloc] init]; |
return filter; |
} |
После того, как Вы выполните эти шаги для создания фильтра, можно использовать фильтр в собственном приложении. Посмотрите Используя Свой Собственный Фильтр для подробных данных. Если Вы хотите сделать фильтр или набор фильтров доступными как плагин для других приложений, посмотрите Упаковку и Загрузку Модулей Изображения.
Используя Ваш собственный фильтр
Процедура для использования Вашего собственного фильтра совпадает с процедурой для использования любого фильтра, предоставленного Базовым Изображением за исключением того, что необходимо инициализировать класс фильтра. Вы инициализируете класс фильтра удаления тумана, создаваемый в последнем разделе с этой строкой кода:
[MyHazeFilter class]; |
Перечисление 9-8 показывает, как использовать фильтр удаления тумана. Отметьте подобие между этим кодом и кодом, обсужденным в Обработке Изображений.
Перечисление 9-8 Используя Ваш собственный фильтр
- (void)drawRect: (NSRect)rect |
{ |
CGRect cg = CGRectMake(NSMinX(rect), NSMinY(rect), |
NSWidth(rect), NSHeight(rect)); |
CIContext *context = [[NSGraphicsContext currentContext] CIContext]; |
if(filter == nil) { |
NSURL *url; |
[MyHazeFilter class]; |
url = [NSURL fileURLWithPath: [[NSBundle mainBundle] |
pathForResource: @"CraterLake" ofType: @"jpg"]]; |
filter = [CIFilter filterWithName: @"MyHazeRemover" |
keysAndValues: |
kCIInputImageKey, [CIImage imageWithContentsOfURL: url], |
kCIInputColorKey, [CIColor colorWithRed:0.7 green:0.9 blue:1], |
nil]; |
} |
[filter setValue: @(distance) forKey: @"inputDistance"]; |
[filter setValue: @(slope) forKey: @"inputSlope"]; |
[context drawImage: [filter valueForKey: kCIOutputImageKey] |
atPoint: cg.origin fromRect: cg]; |
} |
Предоставление функции ROI
Область интереса или ROI, определяет область в источнике, из которого сэмплер берет информацию о пикселе для обеспечения для ядра для обработки. Вспомните из Область обсуждения Интереса в Запросах Системы для Фильтров, что координаты рабочей области ROI и DOD или совпадают точно, зависят от друг друга, или не связанные. Базовое Изображение всегда предполагает, что совпадают ROI и DOD. Если это так, для фильтра Вы пишете, тогда Вы не должны предоставлять функцию ROI. Но если это предположение не является истиной для фильтра, Вы пишете, тогда необходимо предоставить функцию ROI. Далее, можно предоставить функцию ROI только для исполнимых фильтров CPU.
Функция ROI, которую Вы предоставляете, вычисляет представляющую интерес область для каждого сэмплера, использующегося ядром. Базовое Изображение вызывает Вашу функцию ROI, передавая ему индекс сэмплера, степень области, представляемой, и любые данные, которые необходимы Вашей подпрограмме. Сигнатура метода должна следовать за этой формой:
- (CGRect) regionOf:(int)samplerIndex |
destRect:(CGRect)r |
userInfo:obj; |
где:
samplerIndex
указывает сэмплер, для которого метод вычисляет ROIr
указывает степень областиobj
указывает любые данные, это необходимо подпрограмме. Можно использоватьobj
параметр, чтобы гарантировать, что Ваша функция ROI получает данные, в которых это нуждается, и что данные корректны (переменные экземпляра фильтра, возможно, изменились).
Базовое Изображение вызывает Вашу подпрограмму для каждого, проходят через фильтр. Ваш метод вычисляет ROI на основе прямоугольника, и информация о пользователе передала ему и возвращает ROI, указанный как a CGRect
тип данных.
Вы регистрируете функцию ROI путем вызова CIKernel
метод setROISelector:
, предоставление ROI функционирует как aMethod
параметр. Например:
[kernel setROISelector:@selector(regionOf:destRect:userInfo:)]
Следующие разделы обеспечивают примеры функций ROI.
Простая функция ROI
Если Ваша функция ROI не требует, чтобы данные были переданы ему в userInfo
параметр, тогда Вы не должны включать тот параметр, как показано в Перечислении 9-9. Код в началах Перечисления 9-9 сэмплер на один пиксель, который является вычислением, используемым находящим край фильтром или любым 3x3 свертка.
Перечисление 9-9 простая функция ROI
- (CGRect)regionOf:(int)samplerIndex destRect:(CGRect)r |
{ |
return CGRectInset(r, -1.0, -1.0); |
} |
Обратите внимание на то, что эта функция игнорирует samplerIndex
значение. Если Ваше ядро использует только один сэмплер, то можно проигнорировать индекс. Если Ваше ядро использует больше чем один сэмплер, необходимо удостовериться, что Вы возвращаете ROI, это подходяще для указанного сэмплера. Вы будете видеть, как сделать это в следующих разделах.
Функция ROI для стеклянного фильтра искажения
Перечисление 9-10 показывает функцию ROI для стеклянного фильтра искажения. Эта функция возвращает ROI для двух сэмплеров. Сэмплер 0
представляет изображение для искажения и сэмплер 1
представляет текстуру, используемую для стекла.
Функция использует userInfo
параметр для предоставления ввода масштабируется, это необходимо сэмплеру 0
. Заметьте, что искажение является началом половиной предоставленного масштаба на всех сторонах.
Вся стеклянная текстура (сэмплер 1
) потребности, на которые сошлются, потому что фильтр использует текстуру в качестве прямоугольного образца. В результате функция возвращает бесконечный прямоугольник как ROI. Бесконечный прямоугольник является соглашением, указывающим для использования всего сэмплера. (Константа CGRectInfinite
определяется в Кварце 2D API.)
Перечисление 9-10 ROI функционирует для стеклянного фильтра искажения
- (CGRect)regionOf:(int)samplerIndex destRect:(CGRect)r userInfo:obj |
{ |
if (samplerIndex == 0) { |
CGFloat s = [obj floatValue] * 0.5f; |
return CGRectInset(r, -s,-s); |
} |
return CGRectInfinite; |
} |
Функция ROI для карты среды
Перечисление 9-11 показывает функцию ROI, возвращающую ROI для ядра, использующего три сэмплера, один из которых является картой среды. ROI для сэмплера 0
и сэмплер 1
совпадите с DOD. По этой причине код возвращается destination
прямоугольник передал ему для сэмплеров кроме сэмплера 2
.
Сэмплер 2
значения использования передали в userInfo
параметр, которые указывают высоту и ширину среды, отображается для создания прямоугольника, указывающего область интереса.
Перечисление 9-11 , Предоставляющее подпрограмму, вычисляющую область интереса
- (CGRect)regionOf:(int)samplerIndex forRect:(CGRect)destination userInfo:(NSArray *)myArray |
{ |
if (samplerIndex == 2) { |
return CGRectMake(0, 0, |
[myArray[0] floatValue], |
[myArray[1] floatValue]); |
} |
return destination; |
} |
Указание порядка сэмплера
Как Вы видели от предыдущих примеров, сэмплеру связали индекс с ним. При предоставлении функции ROI Базовое Изображение передает индекс сэмплера Вам. Индекс сэмплера присваивается на основе его порядка, когда передано apply
метод для фильтра. Вы вызываете apply
из фильтра outputImage
подпрограмма, как показано в Перечислении 9-12.
В этом перечислении заметьте особенно пронумерованные строки кода, устанавливающие сэмплеры и показывающие, как обеспечить их для ядра. Подробное объяснение каждой из этих строк появляется после Перечисления 9-12.
Перечисление 9-12 выходная подпрограмма изображения для фильтра, использующего карту среды
- (CIImage *)outputImage |
{ |
int i; |
CISampler *src, *blur, *env; // 1 |
CIVector *envscale; |
CGSize size; |
CIKernel *kernel; |
src = [CISampler samplerWithImage:inputImage]; // 2 |
blur = [CISampler samplerWithImage:inputHeightImage]; // 3 |
env = [CISampler samplerWithImage:inputEnvironmentMap]; // 4 |
size = [env extent].size; |
envscale = [CIVector vectorWithX:[inputEMapWidth floatValue] |
Y:[inputEMapHeight floatValue]]; |
i = [inputKind intValue]; |
if ([inputHeightInAlpha boolValue]) |
i += 8; |
kernel = roundLayerKernels[i]; |
[kernel setROISelector:@selector(regionOf:forRect:userInfo:)]; // 5 |
return [self apply: kernel,src, blur, env, |
@( pow(10.0, [inputSurfaceScale floatValue]) ), |
envscale, |
inputEMapOpacity, |
kCIApplyOptionDefinition, |
[src definition], |
kCIApplyOptionUserInfo, |
@[ inputEMapWidth, inputEMapHeight ], |
nil]; // 6 |
} |
Вот то, что делает код:
Объявляет переменные для каждого из трех сэмплеров, которые необходимы для ядра.
Устанавливает сэмплер для входного изображения. ROI для этого сэмплера совпадает с DOD.
Устанавливает сэмплер для изображения, используемого для входной высоты. ROI для этого сэмплера совпадает с DOD.
Устанавливает сэмплер для карты среды. ROI для этого сэмплера не совпадает с DOD, что означает, что необходимо предоставить функцию ROI.
Регистрирует функцию ROI в ядре, которое должно использовать ее.
Применяет параметры ядру для создания Базового изображения Изображения (
CIImage
объект). Переданные аргументы должны быть типом, совместимым с функциональной подписью функции ядра (который не показан здесь, но предположите, что они - совместимый тип). Список параметров завершаетсяnil
, как требуется.Порядок параметров сэмплера определяет свой индекс. Первый сэмплер, предоставленный ядру, является индексом
0
. В этом случае этоsrc
сэмплер. Второй сэмплер, предоставленный ядру —blur
— присваивается индекс1
. Третий сэмплер —env
— присваивается индекс2
. Важно проверить Вашу функцию ROI, чтобы удостовериться, что Вы обеспечиваете надлежащий ROI для каждого сэмплера.
Запись неисполнимых фильтров
Фильтр, который является неисполнимой программой CPU, как гарантируют, будет безопасен. Поскольку этот тип фильтра работает только на GPU, это не может участвовать в вирусе или действии Троянского коня или другом злонамеренном поведении. Для гарантии безопасности неисполнимые фильтры CPU имеют эти ограничения:
Этот тип фильтра является чистым ядром, означая, что это полностью содержится в a
.cikernel
файл. Также, это не имеет класса фильтра и ограничивается в типах обработки, это может обеспечить. Выбирающие инструкции следующей формы являются единственными типами выборки инструкций, которые допустимы для неисполнимого фильтра:color = sample (someSrc, samplerCoord(someSrc));
Неисполнимые фильтры CPU должны быть упакованы как часть модуля изображения.
Базовое Изображение предполагает, что ROI совпадает с DOD. Это означает, что неисполнимые фильтры не подходят для таких эффектов как размытость или искажение.
Выборка CIDemoImageUnit содержит неисполнимую программу, просачиваются MyKernelFilter.cikernel
файл. Когда модуль изображения загружается, фильтр MyKernelFilter загружается вместе с фильтром FunHouseMirror, это находится также в модуле изображения. FunHouseMirror, однако, является исполнимым фильтром. Это имеет часть Objective C, а также часть ядра.
Когда Вы пишете неисполнимый фильтр, необходимо обеспечить все атрибуты фильтра в Descriptions.plist
файл для пакета модуля изображения. Перечисление 9-13 показывает атрибуты для MyKernelFilter в выборке CIDemoImageUnit.
Перечисление 9-13 список свойств для неисполнимого фильтра MyKernelFilter
<key>MyKernelFilter</key> |
<dict> |
<key>CIFilterAttributes</key> |
<dict> |
<key>CIAttributeFilterCategories</key> |
<array> |
<string>CICategoryStylize</string> |
<string>CICategoryVideo</string> |
<string>CICategoryStillImage</string> |
</array> |
<key>CIAttributeFilterDisplayName</key> |
<string>MyKernelFilter</string> |
<key>CIInputs</key> |
<array> |
<dict> |
<key>CIAttributeClass</key> |
<string>CIImage</string> |
<key>CIAttributeDisplayName</key> |
<string>inputImage</string> |
<key>CIAttributeName</key> |
<string>inputImage</string> |
</dict> |
<dict> |
<key>CIAttributeClass</key> |
<string>NSNumber</string> |
<key>CIAttributeDefault</key> |
<real>8</real> |
<key>CIAttributeDisplayName</key> |
<string>inputScale</string> |
<key>CIAttributeIdentity</key> |
<real>8</real> |
<key>CIAttributeMin</key> |
<real>1</real> |
<key>CIAttributeName</key> |
<string>inputScale</string> |
<key>CIAttributeSliderMax</key> |
<real>16</real> |
<key>CIAttributeSliderMin</key> |
<real>1</real> |
</dict> |
<dict> |
<key>CIAttributeClass</key> |
<string>NSNumber</string> |
<key>CIAttributeDefault</key> |
<real>1.2</real> |
<key>CIAttributeDisplayName</key> |
<string> inputGreenWeight </string> |
<key>CIAttributeIdentity</key> |
<real>1.2</real> |
<key>CIAttributeMin</key> |
<real>1</real> |
<key>CIAttributeName</key> |
<string>inputGreenWeight</string> |
<key>CIAttributeSliderMax</key> |
<real>3.0</real> |
<key>CIAttributeSliderMin</key> |
<real>1</real> |
</dict> |
</array> |
</dict> |
<key>CIFilterClass</key> |
<string>MyKernelFilter</string> |
<key>CIHasCustomInterface</key> |
<false/> |
<key>CIKernelFile</key> |
<string>MyKernelFilter</string> |
Примеры подпрограммы ядра
Сущность любого фильтра обработки изображений является ядром, выполняющим пиксельные вычисления. Листинги кода в этом разделе показывают некоторые типичные подпрограммы ядра для этих фильтров: прояснитесь, умножьтесь, и искажение дыры. Путем рассмотрения их можно понять то, как записать собственную подпрограмму ядра. Отметьте, однако, что эти подпрограммы являются примерами. Не предполагайте, что код, показанный здесь, - то, что Базовое Изображение использует для фильтров, которые это предоставляет.
Перед записью собственной подпрограммы ядра можно хотеть считать Выражение Операций Обработки изображений в Базовом Изображении для наблюдения, какие операции ставят проблему в Базовом Изображении. Вы также захотите смотреть на Базовую Ссылку Языка Ядра Изображения.
Можно найти всестороннюю информацию о записи ядер, а также большего количества примеров в Учебном руководстве по Модулю Изображения.
Вычисления проясняющегося эффекта
Перечисление 9-14 вычисляет проясняющийся эффект. Подробное объяснение каждой пронумерованной строки кода появляется после перечисления.
Перечисление 9-14 подпрограмма ядра, вычисляющая проясняющийся эффект
kernel vec4 brightenEffect (sampler src, float k) |
{ |
vec4 currentSource = sample (src, samplerCoord (src)); // 1 |
currentSource.rgb = currentSource.rgb + k * currentSource.a; // 2 |
return currentSource; // 3 |
} |
Вот то, что делает код:
Ищет исходный пиксель в сэмплере, связанном с позицией текущей производительности.
Добавляет смещение к пиксельному значению. Смещение
k
масштабируемый альфа-значением пикселя для проверки пиксельное значение предварительно умножается.Возвращает измененный пиксель.
Вычисления умножить эффект
Перечисление 9-15 показывает подпрограмму ядра, вычисляющую умножить эффект. Код ищет исходный пиксель в сэмплере и затем умножается, это значением передало подпрограмме.
Перечисление 9-15 подпрограмма ядра, вычисляющая умножить эффект
kernel vec4 multiplyEffect (sampler src, __color mul) |
{ |
return sample (src, samplerCoord (src)) * mul; |
} |
Вычисления искажения дыры
Перечисление 9-16 показывает подпрограмму ядра, вычисляющую искажение дыры. Обратите внимание на то, что существует много способов вычислить искажение дыры. Подробное объяснение каждой пронумерованной строки кода появляется после перечисления.
Перечисление 9-16 подпрограмма ядра, вычисляющая искажение дыры
kernel vec4 holeDistortion (sampler src, vec2 center, vec2 params) // 1 |
{ |
vec2 t1; |
float distance0, distance1; |
t1 = destCoord () - center; // 2 |
distance0 = dot (t1, t1); // 3 |
t1 = t1 * inversesqrt (distance0); // 4 |
distance0 = distance0 * inversesqrt (distance0) * params.x; // 5 |
distance1 = distance0 - (1.0 / distance0); // 6 |
distance0 = (distance0 < 1.0 ? 0.0 : distance1) * params.y; // 7 |
t1 = t1 * distance0 + center; // 8 |
return sample (src, samplerTransform (src, t1)); // 9 |
} |
Вот то, что делает код:
Берет три параметра — сэмплер, вектор, указывающий центр искажения дыры, и
params
вектор, содержащий (1/radius
,radius
).Создает вектор
t1
от центра до текущих рабочих координат.Придает расстоянию квадратную форму от центра и присваивает значение
distance0
переменная.Нормализует
t1
. (Делаетt1
единичный вектор.)Вычисляет параметрическое расстояние от центра
(distance squared * 1/distance) * 1/radius
. Это значение 0 в центре и 1, где расстояние равно радиусу.Создает дыру с надлежащим искажением вокруг этого. (
x – 1/sqrt (x)
)Удостоверяется, что все пиксели в карте дыры от пикселя в центре, затем увеличивает масштаб искаженной функции расстояния радиусом.
Масштабирует вектор для создания искажения и затем включает центр назад.
Возвращает искаженную выборку из исходной текстуры.