【问题标题】:How to implement fast image filters on iOS platform如何在 iOS 平台上实现快速图像过滤器
【发布时间】:2011-10-30 15:15:35
【问题描述】:

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

  • 级别调整
  • 亮度/对比度
  • 色相/饱和度
  • 单层和多层覆盖

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

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

https://gist.github.com/1156760

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

任何建议都会有所帮助。

谢谢,
安德烈。

【问题讨论】:

  • 您也应该在 Apple 开发者论坛上发布这个问题。

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


【解决方案1】:

从 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]];
    }

}

注意对比度的默认值为 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]);
}

【讨论】:

    【解决方案2】:

    这是一个旧线程,但我从 SO 上的另一个链接得到它,所以人们仍然阅读它。

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

    Core Image 在后台使用 OpenGL 着色器,因此速度非常快。然而,它比 OpenGL 更容易使用。如果您还没有在 OpenGL 中工作,并且只想将过滤器应用于 CGImage 或 UIIMage 对象,那么 Core Image 过滤器是您的最佳选择。

    【讨论】:

      【解决方案3】:

      如果您正在寻找绝对最快的方法来执行此操作,您将希望使用 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 上处理的任何东西都快得多。

      【讨论】:

        【解决方案4】:

        对于示例代码中的过滤器,您可以使用查找表使其更快。我假设您的输入图像是每种颜色 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 个系数放入代码中。

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

        【讨论】:

        • 太棒了,非常感谢!我将在我的代码中实现查找表。
        【解决方案5】:

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

        同时检查Instagram Image Filters on iPhone

        希望对你有帮助……

        【讨论】:

        • 谢谢,Srikar,但实际上 'ios-image-filters' 也很慢(使用相同的逐像素算法)。
        【解决方案6】:

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

        除此之外,一个真正的快速检查:你打电话给pow( ) 还是powf( )?即使您的数据是float,调用pow( ) 也会得到双精度数学库函数,它比单精度变量powf( ) 慢得多(并且您必须为之间的额外转换付费) floatdouble 也是如此)。

        第二个检查:您是否在 Instruments 中配置了您的过滤器?你真的知道执行时间花在了哪里,还是你在猜测?

        【讨论】:

        • 谢谢,用 powf 抓住了!确实,我使用的是这个函数的双重版本。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-06-30
        • 2018-01-25
        • 1970-01-01
        • 1970-01-01
        • 2020-01-15
        • 1970-01-01
        • 2010-10-22
        相关资源
        最近更新 更多