Как реализовать быстрые фильтры изображений на платформе iOS

Я работаю над приложением iOS, где пользователь может применить определенный набор фильтров фотографий. Каждый фильтр в основном состоит из действий Photoshop с определенными параметрами. Это действия:

  • Настройка уровней
  • Яркость/Контраст
  • Оттенок/Насыщенность
  • Одиночное и множественное наложение

Я повторил все эти действия в своем коде, используя арифметические выражения, которые проходят через все пиксели в изображении. Но когда я запускаю свое приложение на iPhone 4, для каждого фильтра требуется около 3-4 секунд, что довольно долго для ожидания пользователя. Размер изображения составляет 640 x 640 px, что составляет @2x моего размера, потому что он отображается на дисплее Retina. Я обнаружил, что моя главная проблема - это модификации уровней, которые каждый раз вызывает функцию pow() C, когда мне нужно настроить гамму. Конечно, я использую поплавки, а не двойные, потому что ARMv6 и ARMv7 медленны с удвоениями. Попробовал включить и отключить Thumb и получил тот же результат.

Пример простейшего фильтра в моем приложении, который выполняется довольно быстро, хотя (2 секунды). Другие фильтры включают больше выражений и вызовы pow(), что делает их медленными.

https://gist.github.com/1156760

Я видел некоторые решения, которые используют преобразования матриц VDSP Accelerate Framework для быстрой модификации изображений. Я также видел решения OpenGL ES. Я не уверен, что они способны удовлетворить мои потребности. Но, вероятно, это просто вопрос перевода моего набора изменений в какую-нибудь хорошую матрицу свертки?

Любой совет будет полезен.

Спасибо,
Андрей.

Ответ 1

Для фильтра в вашем примере кода вы можете использовать таблицу поиска, чтобы сделать ее намного быстрее. Я предполагаю, что ваше входное изображение имеет 8 бит на цвет, и вы конвертируете его в float, прежде чем передавать его этой функции. Для каждого цвета это дает только 256 возможных значений и, следовательно, только 256 возможных выходных значений. Вы можете предварительно скопировать их и сохранить в массиве. Это позволило бы избежать вычисления pow() и проверки границ, поскольку вы могли бы включить их в предвычисление.

Он будет выглядеть примерно так:

unsigned char table[256];
for(int i=0; i<256; i++) {
    float tmp = pow((float)i/255.0f, 1.3f) * 255.0; 
    table[i] = tmp > 255 ? 255 : (unsigned char)tmp;
}

for(int i=0; i<length; ++i)
    m_OriginalPixelBuf[i] = table[m_OriginalPixelBuf[i]];

В этом случае вам нужно выполнить pow() 256 раз вместо 3 * 640 * 640 раз. Вы также избегаете ветвления, вызванного проверкой границ в вашем основном цикле изображения, который может быть дорогостоящим. Вам не нужно было бы конвертировать в float.

Даже более быстрый способ может состоять в том, чтобы прекомпилировать таблицу вне программы и просто поместить в код 256 коэффициентов.

Ни одна из операций, которые вы там указали, должна требовать свертки или даже матрицы. Все они являются пиксельными операциями, а это означает, что каждый выходной пиксель зависит только от одного соответствующего входного пикселя. Вам нужно будет рассмотреть свертку для операций, таких как размытие или резкость, когда несколько входных пикселей влияют на один выходной пиксель.

Ответ 2

Если вы ищете самый быстрый способ сделать это, вы захотите использовать графический процессор для обработки обработки. Он создан для выполнения массовых параллельных операций, таких как регулировки цвета на одиночных пикселях.

Как я уже упоминал в других ответах, я измерил улучшение производительности на 14X - 28X при запуске операции обработки изображений с использованием OpenGL ES, а не на процессоре. Вы можете использовать фреймворк Accelerate для ускорения обработки изображений на процессорах (я полагаю, что Apple может потребовать около 4-5 раз), но это будет не так быстро, как OpenGL ES. Однако это может быть проще реализовать, поэтому я иногда использовал Accelerate для этого по OpenGL ES.

iOS 5.0 также выводит Core Image с рабочего стола, что дает вам приятную обертку вокруг этих типов корректировок изображений на GPU. Однако есть некоторые ограничения для реализации iOS Core Image, которые у вас отсутствуют при работе с шейдерами OpenGL ES 2.0 напрямую.

Я представляю пример фильтра изображений Shader OpenGL ES 2.0 в моей статье здесь. Самая сложная часть этого вида обработки - создание подсистем OpenGL ES. Используя мое примерное приложение, вы сможете извлечь этот код установки и применить его собственные фильтры, используя его. Чтобы сделать это проще, я создал фреймворк с открытым исходным кодом под названием GPUImage, который обрабатывает все взаимодействие OpenGL ES для вас. Он имеет почти каждый фильтр, который вы перечисляете выше, и большинство из них работают менее чем на 2,5 мс для видеофрагмента 640x480 на iPhone 4, поэтому они намного быстрее, чем все, что обрабатывается на процессоре.

Ответ 3

Как я уже сказал в комментарии, вы должны опубликовать этот вопрос на официальных форумах разработчиков Apple.

В стороне, одна реальная быстрая проверка: звоните ли вы pow( ) или powf( )? Даже если ваши данные float, вызов pow( ) даст вам функцию математической библиотеки с двойной точностью, что значительно медленнее, чем вариант с одной точностью powf( ) (и вам придется заплатить за дополнительные конверсии между float и double).

И вторая проверка: вы профилировали свои фильтры в Инструментах? Вы действительно знаете, где тратится время выполнения, или вы угадываете?

Ответ 4

Я действительно хотел сделать все это сам, но нашел Silverberg Фильтры изображений. На изображениях можно применить различные фильтры изображений типа instagram. Это намного лучше, чем другие фильтры изображений - GLImageProcessing или Cimg.

Также проверьте Фильтры изображений на экране iPhone.

Надеюсь, что это поможет...

Ответ 5

Из iOS 5 вверх вы можете использовать фильтры Core Image для настройки хорошего диапазона параметров изображения.

Чтобы настроить контраст, например, этот код работает как шарм:

    - (void)setImageContrast:(float)contrast forImageView:(UIImageView *)imageView {

    if (contrast > MIN_CONTRAST && contrast < MAX_CONTRAST) {
        CIImage *inputImage = [[CIImage alloc] initWithImage:imageView.image];
        CIFilter *exposureAdjustmentFilter = [CIFilter filterWithName:@"CIColorControls"];
        [exposureAdjustmentFilter setDefaults];
        [exposureAdjustmentFilter setValue:inputImage forKey:@"inputImage"];
        [exposureAdjustmentFilter setValue:[NSNumber numberWithFloat:contrast] forKey:@"inputContrast"]; //default = 1.00
//        [exposureAdjustmentFilter setValue:[NSNumber numberWithFloat:1.0f] forKey:@"inputSaturation"]; //default = 1.00
//        [exposureAdjustmentFilter setValue:[NSNumber numberWithFloat:0.0f] forKey:@"inputBrightness"];
        CIImage *outputImage = [exposureAdjustmentFilter valueForKey:@"outputImage"];
        CIContext *context = [CIContext contextWithOptions:nil];
        imageView.image = [UIImage imageWithCGImage:[context createCGImage:outputImage fromRect:outputImage.extent]];
    }

}

N.B. Значение по умолчанию для контраста - 1,0 (максимальное значение - 4,0).
Кроме того, контраст вычисляется здесь на изображении imageView, поэтому вызов этого метода неоднократно будет накапливать контраст. Значение, если вы сначала вызываете этот метод с значением контраста 2.0, а затем снова с контрастным значением 3.0, вы получите исходное изображение с увеличенным контрастностью на 6.0 (2.0 * 3.0) - не 5.0.

Проверьте документацию Apple для получения дополнительных фильтров и параметров.

Чтобы просмотреть все доступные фильтры и параметры в коде, просто запустите этот цикл:

NSArray* filters = [CIFilter filterNamesInCategories:nil];
for (NSString* filterName in filters)
{
    NSLog(@"Filter: %@", filterName);
    NSLog(@"Parameters: %@", [[CIFilter filterWithName:filterName] attributes]);
}

Ответ 6

Это старый поток, но я получил его из другой ссылки на SO, поэтому люди все еще читают его.

В iOS 5 Apple добавила поддержку Core Image и приличное количество фильтров изображений Core. Я уверен, что все, о которых упомянул OP,

Core Image использует шейдеры OpenGL под обложками, поэтому он очень быстро. Это гораздо проще в использовании, чем OpenGL. Если вы еще не работаете в OpenGL и просто хотите применять фильтры к объектам CGImage или UIIMage, фильтры Core Image - это путь.