3

好的,我正在构建这个应用程序,它在 iPhone/iPad 中进行一些用户交互式图像操作。这基本上是一个依赖于触摸的函数,它根据触摸重新塑造任何图像。在应用程序 Facetune 中可以找到非常相似的东西。

我的算法需要根据触摸运动计算控制点。然后基于这些控制点,我生成一个用于插入图像的结果网格。我的整个方法运行良好。唯一的问题是实时速度很慢。我在用

dispatch_async(dispatch_get_main_queue(), ^{

在触摸移动功能中,但它仍然很慢。将图像重新缩放到大约 360* 可以加快处理速度,但会降低图像质量。

我的触摸移动代码是:

- (void)updateImage
{
    int bytesPerRow = CGImageGetBytesPerRow([staticBG.image CGImage]);
    int height = CGImageGetHeight([staticBG.image CGImage]);
    int width = CGImageGetWidth([staticBG.image CGImage]);
    CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider([staticBG.image CGImage]));
    unsigned char *baseImage = (unsigned char *)CFDataGetBytePtr(pixelData);

    unsigned char *output_image;
    output_image = [self wrappingInterpolation :baseImage :orig :changed :width :height :bytesPerRow];

    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
    CGContextRef context = CGBitmapContextCreate(output_image, width, height, 8, bytesPerRow, colorSpaceRef, bitmapInfo);
    CGImageRef imageRef = CGBitmapContextCreateImage (context);
    UIImage *newimage = [UIImage imageWithCGImage:imageRef];
    CGColorSpaceRelease(colorSpaceRef);
    CGContextRelease(context);
    CFRelease(imageRef);

    free(output_image);
    resultView.image = newimage;
    //staticBG.hidden = YES;
}

wrappingInterpolation 方法对图像像素进行实际的图像处理,该方法经过高度优化以可能实时运行。orig 和 changed 参数只是两个 float*float matix ,它具有我需要的控制点。我在这里看到一个恒定的开销,每次都从 UIImage 获取像素数据,然后创建 CGImageRef、CGContextRef、ColorSpaceRef 等。然后再次释放它。无论如何我可以优化这些?如果可能,建议任何其他可能的加速。但我真的不知道 OpenGL 或着色器,所以我可能无法通过着色器来完成这些。

4

1 回答 1

1

你是对的,瓶颈在于内存管理。

当您将原始位图数据提取到 上CFDataRef pixelData时,您将解压整个图像,并将其完全复制。然后插值例程再次创建数据的副本,该副本经过详细说明并存储到output_image. 然后你释放内存两次,一次用于通过属性分配释放的旧图像,一次用于free(...)命令。

在数字方面:iPhone 5 以 3264x2448 的分辨率拍摄照片,这意味着 22MB 的 RGB 格式(每通道 8b),通过包装程序增加了一倍。并且这种双重分配和双重复制(以及双重释放)以触发事件的速率发生。您还必须添加常规用于处理输入数据的时间:循环每个像素意味着 8MP 图像中的 800 万次迭代。这肯定需要大量的计算能力。

请注意,您要实现的目标在某种程度上类似于实时视频/图像处理;至于视频处理,你应该采用特定的技术。OpenGL 确实是您的最佳选择,但需要付出更多努力。一些基本的建议可能是

  • 避免分配两次:因为您已经在复制数据,baseImage如果您的算法允许就地转换,请直接处理。

  • 缓存图像数据:不要每次都提取位图数据。如果您的算法“附加地”CFDataRef工作,请保持相同并每次都对其进行处理。

    在您的例程中,您始终staticBG将其用作输入数据:这表明算法始终会更改基础图像。您可以一次性缓存原始图像数据。

  • 避免换行UIImage:CALayer有一个有用的属性contents,可以设置为 aCGImageRef来显示内容。此外,您所做的与动画有关,因此您真的可以放弃UI的内容并在CA级别上工作,这更灵活(而且速度更快)。请参阅Apple 的文档

  • 如果您确实必须使用中间内存存储,请重用相同的内存并复制到它:图像的大小可能不会改变,并且您至少可以节省分配时间。

  • 预渲染最终结果:您实际上可以只在屏幕大小上工作,并存储您需要的信息来制作完整大小的图像。然后在后续阶段进行。

这些建议可以避免所有不必要的分配,剩下的只是一个复制操作,如果你的算法不是加法的或者不能就地运行。针对您的算法的一些建议(您可能已经实施):

  • 如果可能,就地处理数据。
  • 仅在实际要修改的像素上循环。
  • 仅复制您需要处理的数据。
  • 如果可以,将像素视为向量并使用优化的函数。看看Accelerate 框架。有许多直接处理图像的例程。
  • 尽可能使用Quartz 2D

不过,最好的建议是:切换到 OpenGL。即使在最好的情况下,你也有一个问题:调用CGBitmapContextCreateImage,你无法避免。文档说您的数据是由 CoreGraphics 复制的:这意味着每一帧都需要至少一个完整的数据副本。据我所知,您无法实时修改正在显示的缓冲区。OpenGL 给出了最好的近似值,它几乎在硬件级别上运行,并写入一个缓冲区,该缓冲区直接刷新到EAGLContextCoreAnimation 显示的一个缓冲区中。

这是一种非常不同的方法,但速度非常快。一些视觉效果(如高斯模糊)需要相当多的能量,但在 OpenGL 下实时运行。您可以开始查看此GLImageProcessing,这是一个非常有趣的光栅图像处理示例。此外,OpenGL 也非常适合图像变形:您可以将图像显示为平面网格上的纹理,然后对其进行变形。

于 2014-01-30T22:45:52.627 回答