如何在iOS平台上实现快速图像过滤器

时间:2011-08-19 13:23:01

标签: ios image-processing opengl-es matrix-multiplication accelerate-framework

我正在开发iOS应用程序,用户可以在其中应用一组特定的照片过滤器。每个过滤器基本上都是具有特定参数的Photoshop动作集。这些行动是:

  • 级别调整
  • 亮度/对比度
  • 色调/饱和度
  • 单个和多个叠加

我在代码中使用循环遍历图像中所有像素的算术表达式重复了所有这些操作。但是当我在iPhone 4上运行我的应用程序时,每个过滤器需要大约3-4秒才能应用,这是用户等待的相当多的时间。图像尺寸为640 x 640像素,这是我视图尺寸的2倍,因为它显示在Retina显示屏上。我发现我的主要问题是每次需要调整gamma时调用pow()C函数的级别修改。我当然使用的浮动不是双打,因为ARMv6和ARMv7的双打速度很慢。尝试启用和禁用Thumb并获得相同的结果。

我的应用程序中最简单的过滤器示例,它运行得非常快(2秒)。其他过滤器包含更多表达式和pow()调用,从而使它们变慢。

https://gist.github.com/1156760

我见过一些使用Accelerate Framework vDSP矩阵变换进行快速图像修改的解决方案。我也看过OpenGL ES解决方案。我不确定他们能满足我的需求。但可能只是将我的一组变化转换为一些好的卷积矩阵?

任何建议都会有所帮助。

谢谢,
安德烈。

6 个答案:

答案 0 :(得分:13)

对于示例代码中的过滤器,您可以使用查找表来加快速度。我假设您的输入图像是每种颜色8位,并且在将其传递给此函数之前将其转换为浮点数。对于每种颜色,这仅提供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次。您还可以避免由主图像循环中的边界检查导致的分支,这可能是昂贵的。您也不必转换为浮动。

更快的方法可能是在程序外预先计算表格,然后将256个系数放在代码中。

你在那里列出的所有操作都不需要卷积甚至是矩阵乘法。它们都是逐像素操作,这意味着每个输出像素仅取决于单个相应的输入像素。对于多个输入像素影响单个输出像素的模糊或锐化等操作,您需要考虑卷积。

答案 1 :(得分:10)

如果您正在寻找绝对最快的方法,那么您将需要使用GPU来处理。它是为大规模并行操作而构建的,例如对单个像素进行颜色调整。

正如我在other answers中提到的,当使用OpenGL ES而不是CPU运行图像处理操作时,我测得性能提高了14倍-28倍。您可以使用Accelerate框架来更快地进行CPU图像处理(我相信Apple声称可以提供大约4-5倍的提升),但它不会像OpenGL ES那样快。然而,它可以更容易实现,这就是为什么我有时在OpenGL ES上使用Accelerate。

iOS 5.0还带来了桌面上的Core Image,它为您提供了围绕这些GPU上图像调整的精美包装。但是,在直接使用OpenGL ES 2.0着色器时,您没有iOS Core Image实现的一些限制。

我在文章here中提供了一个OpenGL ES 2.0着色器图像过滤器的示例。进行这种处理最困难的部分是设置OpenGL ES脚手架。 在那里使用我的示例应用程序,您应该能够提取该设置代码并使用它来应用您自己的过滤器。为了使这更容易,我创建了一个名为GPUImage的开源框架,为您处理所有OpenGL ES交互。它几乎包含了您在上面列出的每个过滤器,并且大多数在iPhone 4上的640x480帧视频下运行时间不到2.5毫秒,因此它们比在CPU上处理的任何内容都要快得多。

答案 2 :(得分:3)

正如我在评论中所说,你也应该在官方Apple开发者论坛上发布这个问题。

除此之外,还有一个真正的快速检查:您是在呼叫pow( )还是powf( )?即使您的数据是float,调用pow( )也会获得双精度数学库函数,这比单精度变量powf( )慢得多(并且您必须支付floatdouble之间的额外转化费用。

第二次检查:您是否在仪器中分析了过滤器?你真的知道执行时间花在哪里,或者你在猜什么?

答案 3 :(得分:3)

我其实想要自己做所有这些,但我找到了Silverberg的 Image Filters 。您可以在图像上应用各种Instagram类型的图像过滤器。这比其他图像过滤器好得多 - GLImageProcessing或Cimg。

同时检查Instagram Image Filters on iPhone

希望这会有所帮助......

答案 4 :(得分:2)

从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 documentation以获取更多过滤器和参数。

要在代码中列出所有可用的过滤器和参数,只需运行此循环:

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

答案 5 :(得分:0)

这是一个老线程,但是我从SO上的另一个链接开始,所以人们仍然会阅读它。

在iOS 5中,Apple增加了对Core Image的支持,以及相当数量的Core图像过滤器。我很确定OP提到的所有OP都可用

Core Image使用OpenGL着色器,所以它非常快。然而,它比OpenGL更容易使用。如果您尚未使用OpenGL,并且只想将过滤器应用于CGImage或UIIMage对象,则可以使用Core Image过滤器。