Как создать простой пользовательский фильтр для iOS с помощью Core Image Framework? - PullRequest
5 голосов
/ 17 января 2012

Я хочу использовать в своем приложении фильтр custom . Теперь я знаю, что мне нужно использовать Core Image framework, но я не уверен, что это правильный путь. Базовый каркас использует для Mac OS и iOS 5.0 - я не уверен, что это можно использовать для создания пользовательских эффектов CIFilter. Можете ли вы помочь мне с этим вопросом? Спасибо всем!

Ответы [ 4 ]

21 голосов
/ 19 января 2012

Как утверждает Адам, в настоящее время Core Image на iOS не поддерживает пользовательские ядра, как в старой реализации Mac. Это ограничивает то, что вы можете сделать с фреймворком, до некоторой комбинации существующих фильтров.

(Обновление: 13.02.2012)

По этой причине я создал инфраструктуру с открытым исходным кодом для iOS под названием GPUImage , которая позволяет создавать пользовательские фильтры, которые будут применяться к изображениям и видео с использованием фрагментных шейдеров OpenGL ES 2.0. Я опишу больше о том, как работает этот фреймворк в моем посте на тему . По сути, вы можете предоставить свои собственные фрагменты шейдеров OpenGL Shading Language (GLSL), чтобы создать собственный фильтр, а затем запустить этот фильтр для статических изображений или живого видео. Эта платформа совместима со всеми устройствами iOS, поддерживающими OpenGL ES 2.0, и может создавать приложения для iOS 4.0.

Например, вы можете настроить фильтрацию живого видео, используя следующий код:

GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];
GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, viewWidth, viewHeight)];

// Add the view somewhere so it's visible

[videoCamera addTarget:thresholdFilter];
[customFilter addTarget:filteredVideoView];

[videoCamera startCameraCapture];

В качестве примера пользовательской программы фрагментного шейдера, которая определяет фильтр, ниже применяется эффект тона сепии:

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

void main()
{
    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
    lowp vec4 outputColor;
    outputColor.r = (textureColor.r * 0.393) + (textureColor.g * 0.769) + (textureColor.b * 0.189);
    outputColor.g = (textureColor.r * 0.349) + (textureColor.g * 0.686) + (textureColor.b * 0.168);    
    outputColor.b = (textureColor.r * 0.272) + (textureColor.g * 0.534) + (textureColor.b * 0.131);

    gl_FragColor = outputColor;
}

Язык, используемый для написания пользовательских ядер Core Image на Mac, очень похож на GLSL. Фактически, вы сможете сделать несколько вещей, которые вы не можете сделать в настольном Core Image, потому что в языке ядра Core Image отсутствуют некоторые вещи, которые есть у GLSL (например, ветвление).

9 голосов
/ 15 января 2015

Первоначально принятый ответ устарел.С iOS 8 вы можете создавать собственные ядра для фильтров.Вы можете найти больше информации об этом в:

5 голосов
/ 17 января 2012

УСТАРЕЛО

Вы не можете создавать свои собственные ядра / фильтры в iOS.См. http://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/CoreImaging/ci_intro/ci_intro.html,, в частности:

Хотя этот документ включен в справочную библиотеку, он не был подробно обновлен для iOS 5.0.В следующей редакции будут подробно описаны различия в Core Image на iOS.В частности, ключевое отличие состоит в том, что Базовое изображение на iOS не включает в себя возможность создавать пользовательские фильтры изображений .

(Bolding mine)

0 голосов
/ 21 октября 2015

Пользовательские фильтры для 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
...