为什么从相机缩小UIImage这么慢?

时间:2009-12-05 06:34:03

标签: iphone performance uiimage uiimagepickercontroller

调整UIImagePickerController返回的相机UIImage的大小如果按照this post中的常规方式执行,则需要花费相当长的时间。

[更新:此处有创意的最后征集!我的下一个选择是去询问Apple,我想。]

是的,这是很多像素,但iPhone上的图形硬件完全能够在1/60秒内在屏幕上绘制大量1024x1024纹理四边形,因此确实应该有一种方法来调整2048x1536的大小在不到1.5秒的时间内将图像缩小到640x480。

那为什么这么慢?操作系统从选择器返回的底层图像数据是否已经准备好被绘制,因此必须以某种方式将其调整为GPU无法帮助?

我最好的猜测是它需要从RGBA转换为ABGR或类似的东西;任何人都可以想到一种可能说服系统快速给我数据的方式,即使它的格式错误,我稍后会自己处理它?<​​/ p>

据我所知,iPhone没有任何专用的“图形”内存,所以不应该有将图像数据从一个地方移动到另一个地方的问题。

所以,问题是:除了使用更有利于GPU的CGBitmapContextCreate和CGContextDrawImage之外,还有一些替代绘图方法吗?

要研究的东西:如果我开始使用与图像选择器不同的大小相同的UIImage,它是否同样慢?显然不是......

更新:Matt Long发现,如果您已启用手动相机控件裁剪,则只需30ms即可调整从[info objectForKey:@"UIImagePickerControllerEditedImage"]中的拾取器返回的图像大小。这对我关心我使用takePicture以编程方式拍摄照片的情况没有帮助。我看到编辑后的图片为kCGImageAlphaPremultipliedFirst,但原始图片为kCGImageAlphaNoneSkipFirst

进一步更新:杰森克劳福德提出CGContextSetInterpolationQuality(context, kCGInterpolationLow),实际上确实将时间从大约1.5秒缩短到1.3秒,但成本与图像质量有关 - 但这仍然远远超出GPU应该具备的速度!的

本周用完之前的最后更新:用户refulgentis做了一些分析,这似乎表明花了1.5秒将捕获的摄像机图像作为JPEG写入磁盘,然后再将其读回如果是真的,非常离奇。

5 个答案:

答案 0 :(得分:3)

似乎你在这里做了几个假设,可能是也可能不是。我的经历与你的不同。通过调用以下方式将照片从相机拍摄到原始大小的0.31时,This method似乎只需要20到30毫秒的时间。

CGImageRef scaled = CreateScaledCGImageFromCGImage([image CGImage], 0.31);

(顺便说一下,我得到0.31的宽度尺,640.0 / 2048.0)

我已经检查过以确保图片尺寸与您正在使用的尺寸相同。这是我的NSLog输出:

2009-12-07 16:32:12.941 ImagePickerThing[8709:207] Info: {
    UIImagePickerControllerCropRect = NSRect: {{0, 0}, {2048, 1536}};
    UIImagePickerControllerEditedImage = <UIImage: 0x16c1e0>;
    UIImagePickerControllerMediaType = "public.image";
    UIImagePickerControllerOriginalImage = <UIImage: 0x184ca0>;
}

我不确定为什么差异,我无法回答你的问题,因为它与GPU有关,但我会认为1.5秒和30ms是一个非常显着的差异。也许将该博客文章中的代码与您正在使用的内容进行比较?

最诚挚的问候。

答案 1 :(得分:2)

我遇到了同样的问题并长时间撞击它。据我所知,第一次访问图像选择器返回的UIImage时,速度很慢。作为一个实验,尝试使用UIImage对任何两个操作进行计时 - 例如,缩小,然后使用UIImageJPEGRepresentation等。然后切换订单。当我在过去做过这个时,第一个操作会受到时间的限制。我最好的假设是,内存仍然以某种方式存在于CCD上,并将其转移到主内存中以便用它做任何事情都很慢。

当您设置allowsImageEditing = YES时,您获取的图像会调整大小并裁剪为约320x320。这使它更快,但它可能不是你想要的。

我发现的最佳加速是:

CGContextSetInterpolationQuality(context, kCGInterpolationLow)

在您执行CGContextDrawImage之前从CGBitmapContextCreate返回的上下文。

问题是您的缩小图像可能看起来不太好。但是,如果您按整数因子缩小 - 例如,1600x1200到800x600 - 那么它看起来没问题。

答案 2 :(得分:2)

使用Shark,对其进行描述,弄清楚需要花费多长时间。

我必须在MediaPlayer.framework上工作很多,当你获得iPod上歌曲的属性时,第一个属性请求与后续请求相比非常慢,因为在第一个属性请求中,MobileMediaPlayer打包了一个包含所有属性并将其传递给我的应用程序。

我愿意打赌这里也会发生类似的情况。

编辑:我能够在Shark中为Matt Long的UIImagePickerControllerEditedImage情况和通用的UIImagePickerControllerOriginalImage情况做一个时间配置文件。

在这两种情况下,大部分时间都由CGContextDrawImage占用。在Matt Long的案例中,UIImagePickerController在捕获图像的用户和进入“编辑”模式的图像之间处理这个问题。

缩放CGContextDrawImage = 100%的时间百分比,然后CGContextDelegateDrawImage占用100%,然后ripc_DrawImage(来自libRIP.A.dylib)占用100%,然后ripc_AcquireImage(它看起来像解压缩JPEG,并占用大部分时间在_cg_jpeg_idct_islow,vec_ycc_bgrx_convert,decompress_onepass,sep_upsample)占用了93%的时间。只有7%的时间花在ripc_RenderImage上,我假设它是实际绘图。

答案 3 :(得分:1)

这是我使用的git项目,似乎运行良好。用法非常简洁 - 一行代码。

https://github.com/AliSoftware/UIImage-Resize

答案 4 :(得分:-1)

在这种情况下不要使用CGBitmapImageContextCreate!我在你所处的相同情况下花了差不多一个星期的时间。性能绝对可怕,会像疯了一样吃掉记忆。改为使用UIGraphicsBeginImageContext:

// create a new CGImage of the desired size
UIGraphicsBeginImageContext(desiredImageSize);
CGContextRef c = UIGraphicsGetCurrentContext();

// clear the new image
CGContextClearRect(c, CGRectMake(0,0,desiredImageSize.width, desiredImageSize.height));

// draw in the image
CGContextDrawImage(c, rect, [image CGImage]);

// return the result to our parent controller
UIImage * result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

在上面的例子中(来自我自己的图像调整大小代码),“rect”明显小于图像。上面的代码运行得非常快,应该完全符合您的需要。

我不完全确定为什么UIGraphicsBeginImageContext如此快,但我相信它与内存分配有关。我注意到这种方法需要的内存要少得多,这意味着操作系统已经在某处为图像上下文分配了空间。