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

10

我正在开发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矩阵变换进行快速图像修改。我也看到了OpenGL ES解决方案。我不确定它们是否能满足我的需要。但可能只是将我的一组更改转换为某个好的卷积矩阵的问题?

任何建议都将非常有用。

谢谢,
Andrey。


你应该在苹果开发者论坛上发布这个问题。 - Stephen Canon
6个回答

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次。您还将避免在主图像循环中引起的边界检查分支,这可能很昂贵。您也无需转换为float。
甚至更快的方法可能是在程序外预先计算表格,然后只需将256个系数放入代码中。
您列出的所有操作都不应该需要卷积或矩阵乘法。它们都是逐像素操作,这意味着每个输出像素仅取决于单个对应的输入像素。对于诸如模糊或锐化等多个输入像素影响单个输出像素的操作,您需要考虑卷积。

太棒了,非常感谢!我将在我的代码中实现查找表。 - Andrey Chernih

10
如果你正在寻找绝对最快的方法来完成这个任务,你需要使用GPU来处理。它专门用于执行大规模并行操作,例如对单个像素进行颜色调整。
如我在其他答案中提到的,当使用OpenGL ES而不是CPU运行图像处理操作时,我测得性能提高了14倍至28倍。你可以使用Accelerate框架来进行更快的CPU上的图像操作(我相信苹果声称这里可能会有约4-5倍的提速),但它不会像OpenGL ES那样快。然而,它可能更容易实现,这就是为什么我有时会选择Accelerate而不是OpenGL ES。
iOS 5.0还从桌面版引入了Core Image,它为这种基于GPU的图像调整提供了一个很好的封装。然而,在使用iOS Core Image实现时,存在一些限制,而在直接使用OpenGL ES 2.0着色器时则没有这些限制。
我在这里的文章中提供了一个OpenGL ES 2.0着色器图像滤镜的示例。做这种处理最困难的部分是设置OpenGL ES脚手架。为了使这更容易,我创建了一个名为GPUImage的开源框架,它为您处理所有的OpenGL ES交互。它几乎拥有上面列出的每个滤镜,并且大多数在iPhone 4上以640x480视频帧为基础运行时间不超过2.5毫秒,因此比在CPU上处理任何内容都要快得多。

3
正如我在评论中所说,你应该在官方的苹果开发者论坛上发布这个问题。
除此之外,有一个真正快速的检查:你是调用了pow()还是powf()?即使你的数据是float类型,调用pow()会获取双精度数学库函数,它比单精度变量powf()慢得多(而且你还需要支付额外的float和double之间的转换费用)。
第二个检查:你是否在Instruments中对你的过滤器进行了分析?你真的知道执行时间花在哪里了吗,还是只是猜测?

谢谢,powf的使用很好!事实上,我一直在使用双版本的这个函数。 - Andrey Chernih

3

实际上我本来打算自己完成这些工作,但是我发现了Silverberg的 Image Filters。你可以在图片上应用不同的instagram类型的图像过滤器,这比其他图像过滤器(如GLImageProcessing或Cimg)要好得多。

还可以查看iPhone上的Instagram图像过滤器

希望这有所帮助...


谢谢,Srikar,但实际上,“ios-image-filters”也很慢(使用相同的逐像素算法)。 - Andrey Chernih

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

}

注意:对比度的默认值为1.0(建议的最大值为4.0)。
此外,在此处计算图像视图的对比度,因此重复调用此方法将累积对比度。也就是说,如果您首先使用对比度值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]);
}

0

这是一个旧的帖子,但我从SO上的另一个链接进入,所以人们仍然在阅读它。

iOS 5中,苹果添加了对Core Image的支持,并提供了相当数量的Core Image滤镜。我很确定OP提到的所有滤镜都可用。

Core Image在底层使用OpenGL着色器,因此速度非常快。但与OpenGL相比,它要容易得多。如果您还没有在OpenGL中工作,并且只想将滤镜应用于CGImage或UIIMage对象,则Core Image滤镜是最好的选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接