Давно, в 2005 году, мне купили первый телефон с камерой — Siemens M65. После того как было сделано приличное количество снимков, возникла нужда их упорядочить — была написана небольшая программа на Delphi, которая упорядочивала и позволяла просматривать изображения по дате — путём выбора нужного года, месяца и числа. Шло время, увеличивались мощности телефонов, количество мегапикселей, появилась поддержка видео — и всё это по мере появления было встроено в программу. Позже появилась версия программы и под Mac OS X, которая была написана с использованием фреймворков, доступных в этой ОС для работы с изображениями (Core Image) и видео (AV Foundation). О первом фреймворке и создании для него своего фильтра поговорим более подробно.
Core Image — технология обработки изображений, которая появилась в Mac OS X 10.4 — позволяет проводить попиксельные операции с исходным изображением при помощи так называемых плагинов. Встроенных плагинов достаточно много — при некотором количестве времени на основе Core Image можно сделать некоторый аналог фотошопа со слоями, режимами наложения, корректировками уровней, яркости и т.п. Причем всё просчёты будут произведены на GPU (при его наличии), что даст существенную скорость в обработке изображений.
Core Image Filter — плагин для обработки изображений, который имеет как минимум 2 функции: принимать входное изображение и выдавать результат. На странице описания встроенных плагинов есть примеры того, как будет выглядеть изображение до и после фильтра. Также плагины делятся на категории:
и выбираем: Mac OS X -> System Plug-in -> Image Unit Plug-in:
Далее вводим имя фильтра (мой вариант — AnaglyphFilter) и сразу добавляем его в сборку проекта(Product — Edit Scheme — Build, список Targets). Я также добавил зависимость сборки главного проекта от фильтра (чтобы фильтр был собран наверняка при сборке проекта, Build Phases — Target Dependencies) и автоматическое копирование фильтра в папку Resources, всё того же главного проекта (Build Phases — Add Build Phase — Add Copy Files). Теперь можно приступить непосредственно к программированию фильтра. Но для начала немного о содержании плагина.
В файле AnaglyphFilterFilter.m есть несколько процедур, привожу описание:
Подпрограмма на языке CIKL будет выглядеть следующим образом:
Core Image
Core Image — технология обработки изображений, которая появилась в Mac OS X 10.4 — позволяет проводить попиксельные операции с исходным изображением при помощи так называемых плагинов. Встроенных плагинов достаточно много — при некотором количестве времени на основе Core Image можно сделать некоторый аналог фотошопа со слоями, режимами наложения, корректировками уровней, яркости и т.п. Причем всё просчёты будут произведены на GPU (при его наличии), что даст существенную скорость в обработке изображений.
Core Image Filter
Core Image Filter — плагин для обработки изображений, который имеет как минимум 2 функции: принимать входное изображение и выдавать результат. На странице описания встроенных плагинов есть примеры того, как будет выглядеть изображение до и после фильтра. Также плагины делятся на категории:
- CICategoryBlur — размытие изображений (пример — размытие по Гауссу)
- CICategoryColorAdjustment — управление цветом (пример — работа с цветовыми каналами)
- CICategoryColorEffect — цветовые эффекты (пример — сепия)
- CICategoryCompositeOperation — наложение изображений (пример — наложение 2-х изображений методом «Экран»)
- CICategoryDistortionEffect — искажения изображений (пример — «закручивание» изображения)
- CICategoryGenerator — генераторы изображений (пример — генератор изображения с заданными цветом)
- CICategoryGeometryAdjustment — управление геометрией изображений (пример — масштабирование, перспектива)
- CICategoryGradient — генераторы градиентов (пример — линейный, радиальный градиенты)
Свой фильтр
Подготовка
Наша же цель — написать собственный плагин-фильтр. А в качестве примера я решил выбрать создание анаглифного изображения для просмотра его красно-бирюзовыми очками. Всё это было реализовано в моей программе, речь о которой шла в начале статьи. Для начала запускаем xcode. Далее определяемся — будем ли мы писать плагин как отдельный проект, либо же добавим его в существующий. В моём случае я добавил новый проект в существующий — для этого чуть ниже окна TARGETS жмём Add Target:и выбираем: Mac OS X -> System Plug-in -> Image Unit Plug-in:
Далее вводим имя фильтра (мой вариант — AnaglyphFilter) и сразу добавляем его в сборку проекта(Product — Edit Scheme — Build, список Targets). Я также добавил зависимость сборки главного проекта от фильтра (чтобы фильтр был собран наверняка при сборке проекта, Build Phases — Target Dependencies) и автоматическое копирование фильтра в папку Resources, всё того же главного проекта (Build Phases — Add Build Phase — Add Copy Files). Теперь можно приступить непосредственно к программированию фильтра. Но для начала немного о содержании плагина.
Взгляд изнутри
После создания нового плагина можно заметить следующие файлы в папке <имя плагина> в дереве проекта:- AnaglyphFilterPlugInLoader .h/.m — класс загрузчика нашего плагина
- AnaglyphFilterFilter .h/.m — класс плагина, который содержит некоторые вспомогательные данные
- index.html — файл с описанием / справкой нашего плагина
- Description.plist — информация о нашем плагине
- Description.strings — файл локализации
- AnaglyphFilterFilterKernel.cikernel — самая интересная часть, программа на языке Core Image Kernel Language (CIKL), которая отвечает за работу с пикселями
Убираем лишнее
Шаблон, который сделал нам xcode, нужно модифицировать под наши нужды. В фильтре будут 2 входных параметра: основное изображение(левое) и правое. На выходе же будет совмещённое анаглифное изображение. Для начала оставляем только нужные нам параметры. Открываем файл AnaglyphFilterFilter.h и приводим класс AnaglyphFilterFilter к следующему виду:@interface AnaglyphFilterFilter : CIFilter
{
CIImage *inputImage;//входное изображение (левое)
CIImage *rightImage;//правое изображение
}
@end
В файле AnaglyphFilterFilter.m есть несколько процедур, привожу описание:
- init — загрузка кода плагина из файла и преобразование его в объект CIKernel, тут менять ничего не нужно
- regionOf — возвращает область изображения, с которой будет работать фильтр. В нашем случае нам нужно всё изображение, поэтому меняем код так:
- (CGRect)regionOf:(int)sampler destRect:(CGRect)rect userInfo:(NSValue*)value
{
return rect;
} - customAttributes — настройка и описание дополнительных аттрибутов фильтра. Добавляем аттрибут правого изображения:
- (NSDictionary *)customAttributes
{
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSDictionary dictionaryWithObjectsAndKeys:
[CIImage class],kCIAttributeClass,
[CIImage emptyImage], kCIAttributeDefault,
nil],@"rightImage",
nil];
} - outputImage — вызов подпрограммы для работы с изображением. В процедуре apply передаются параметры leftSamp и rightSamp, которые будут переданы в подпрограмму CIKL:
- (CIImage *)outputImage
{
CISampler *leftSamp = [CISampler samplerWithImage:inputImage];
CISampler *rightSamp = [CISampler samplerWithImage:rightImage];
return [self apply:_AnaglyphFilterFilterKernel,leftSamp,rightSamp,
kCIApplyOptionDefinition,[src definition],nil];
}
Программа CIKL
Представляет из себя код на языке CIKL, который является разновидностью OpenGL Shading Language. Подпрограммы, которые мы будем вызывать из основного кода, начинаются с ключего слова kernel и могут быть найдены по свойству name класса CIKernel. Но в нашем случае нам нужна только одна процедура, поэтому инициализация проходит следующим образом (в процедуре init):_AnaglyphFilterFilterKernel = [[kernels objectAtIndex:0] retain];Аналогично можно было бы получить процедуру по имени:
_AnaglyphFilterFilterKernel = [[kernels findKernelByName:@"procedureName"] retain];Сама же процедура в файле .cikernel будет выглядеть примерно так:
kernel vec4 procedureName(sampler leftSamp, sampler rightSamp)
{
//код для работы с пикселями
//возврат результата
}
Анаглиф
Существуют разные по сложности способы получения анаглифных изображений. Для нашего случая мы воспользуемся полноцветным режимом наложения картинок:- У левого изображение убираем зеленый и синий каналы, остаётся красный
- У правого изображения убираем красный канал, остаются зелёный и синий
- Совмещаем изображение по методу наложения «Экран»
Подпрограмма на языке CIKL будет выглядеть следующим образом:
kernel vec4 anaglyphRedCyan(sampler leftSamp, sampler rightSamp)
{
//получаем пиксель левого изображения
vec4 l = sample(leftSamp,samplerCoord(leftSamp));
//убираем зеленый и синий каналы
l.g = l.b = 0.0;
//получаем пиксель правого изображения
vec4 r = sample(rightSamp,samplerCoord(rightSamp));
//убираем красный канал
r.r = 0.0;
//накладываем изображением методом "Экран"
vec4 ret;
//красный канал
ret.r = 1.0 - (1.0 - l.r)*(1.0 - r.r);
//зеленый канал
ret.g = 1.0 - (1.0 - l.g)*(1.0 - r.g);
//синий канал
ret.b = 1.0 - (1.0 - l.b)*(1.0 - r.b);
//прозрачность не используется
ret.a = 1.0;
//результат
return ret;
}
Используем фильтр
При загрузке основной программы нужно вставить код, который будет загружать наш фильтр (с учётом того, что мы положили его в папку Resources):- (void)loadCoreImageFiltersТеперь использовать фильтр можно точно так же, как и остальные фильтры:
{
NSString* path = [[NSBundle mainBundle] pathForResource:@"AnaglyphFilter" ofType:@"plugin"];
[CIPlugIn loadPlugIn:[NSURL fileURLWithPath:path] allowExecutableCode:YES];
}
CIFilter* anaglyphFilter = [CIFilter filterWithName:@"AnaglyphFilter"];
[anaglyphFilter setDefaults];
[anaglyphFilter setValue:leftImage forKey:@"inputImage"];
[anaglyphFilter setValue:rightImage forKey:@"rightImage"];
CIImage* anaglyphImage = [anaglyphFilter valueForKey:@"outputImage"];
Заключение
Работа с Core Image оказалась не такой сложной, как может показаться. А вот и результаты работы:- исходное левое изображение:
- исходное правое изображение
- результат