iOS性能调整:获取大图像像素颜色的最快方法

时间:2012-05-01 22:31:38

标签: ios performance uiimage core-graphics

关于如何获得给定点的图像的像素颜色,存在许多问题/答案。但是,对于大型图像(例如,甚至小到1000 x 1300),所有这些答案都真的慢(100-500ms)。

那里的大多数代码示例都绘制到图像上下文。当实际抽签时,所有这些都需要时间:

CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, (CGFloat)width, (CGFloat)height), cgImage)

在Instruments中对此进行检查后发现,通过从源图像复制数据来完成绘制:

enter image description here

我甚至尝试了另一种获取数据的方法,希望自己获得字节实际上会更有效率。

NSInteger pointX = trunc(point.x);
NSInteger pointY = trunc(point.y);
CGImageRef cgImage = CGImageCreateWithImageInRect(self.CGImage, 
                           CGRectMake(pointX * self.scale, 
                                      pointY * self.scale, 
                                      1.0f, 
                                      1.0f));

CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef data = CGDataProviderCopyData(provider);

CGImageRelease(cgImage);

UInt8* buffer = (UInt8*)CFDataGetBytePtr(data);

CGFloat red   = (float)buffer[0] / 255.0f;
CGFloat green = (float)buffer[1] / 255.0f;
CGFloat blue  = (float)buffer[2] / 255.0f;
CGFloat alpha = (float)buffer[3] / 255.0f;

CFRelease(data);

UIColor *pixelColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];

return pixelColor;

此方法花费时间在数据副本上:

CFDataRef data = CGDataProviderCopyData(provider);

它似乎也是从磁盘读取数据,而不是我正在创建的CGImage实例:

enter image description here

现在,这种方法,在一些非正式测试中确实表现得更好,但它仍然没有我想要的那么快。 有没有人知道更快获取基础像素数据的方法???

4 个答案:

答案 0 :(得分:6)

如果您可以通过OpenGL ES将此图像绘制到屏幕上,则可以通过该版本中引入的纹理缓存快速随机访问iOS 5.0中的基础像素。它们允许直接存储器访问存储在OpenGL ES纹理中的底层BGRA像素数据(图像将驻留在其中),并且您几乎可以立即从该纹理中挑选出任何像素。

我用它来回读偶数大(2048x2048)图像的原始像素数据,读取时间最差的时间在10-20毫秒之间,以拉下所有这些像素。同样,对单个像素的随机访问几乎没有时间,因为您只是从字节数组中的某个位置读取。

当然,这意味着您必须解析并将您的特定图像上传到OpenGL ES,这将涉及从磁盘读取相同内容并与Core Graphics进行交互(如果通过UIImage),您将看到你试图从磁盘上的随机PNG读取像素数据,但听起来你需要渲染一次并从中多次采样。如果是这样的话,OpenGL ES和iOS 5.0上的纹理缓存将是读取屏幕上显示的像素数据的绝对最快方式。

我将这些进程封装在我的开源GPUImage框架内的GPUImagePicture(图像上传)和GPUImageRawData(快速原始数据访问)类中,如果你想看看它是如何工作的那样。

答案 1 :(得分:4)

我还没有找到一种方法来访问绘制的(帧内缓冲区)像素。我测量过的最快的方法是:

  1. 通过在创建时指定kCGImageSourceShouldCache来表明您希望缓存图像。
  2. (可选)通过强制渲染来预先处理图像。
  3. 将图像绘制为1x1位图上下文。
  4. 此方法的成本是缓存的位图,只要与其关联的CGImage,该位图可能具有生命周期。代码最终看起来像这样:

    1. 使用ShouldCache标志创建图像

      NSDictionary *options = @{ (id)kCGImageSourceShouldCache: @(YES) };
      CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
      CGImageRef cgimage = CGImageSourceCreateImageAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
      UIImage *image = [UIImage imageWithCGImage:cgimage];
      CGImageRelease(cgimage);
      
    2. 预先图像

      UIGraphicsBeginImageContext(CGSizeMake(1, 1));
      [image drawAtPoint:CGPointZero];
      UIGraphicsEndImageContext();
      
    3. 将图像绘制到1x1位图上下文

      unsigned char pixelData[] = { 0, 0, 0, 0 };
      CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
      CGContextRef context = CGBitmapContextCreate(pixelData, 1, 1, 8, 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
      CGImageRef cgimage = image.CGImage;
      int imageWidth = CGImageGetWidth(cgimage);
      int imageHeight = CGImageGetHeight(cgimage);
      CGContextDrawImage(context, CGRectMake(-testPoint.x, testPoint.y - imageHeight, imageWidth, imageHeight), cgimage);
      CGColorSpaceRelease(colorSpace);
      CGContextRelease(context);
      
    4. pixelData具有testPoint处像素的R,G,B和A值。

答案 2 :(得分:1)

CGImage上下文可能几乎是空的,并且在您尝试读取第一个像素或绘制它之前不包含实际像素数据,因此尝试加速从图像中获取像素可能无法让您随处可见。什么都没有。

您是否尝试从PNG文件中读取像素?您可以尝试直接在文件之后直接进行mmap并自己解码PNG格式。从存储中提取数据仍需要一段时间。

答案 3 :(得分:-2)

- (BOOL)isWallPixel: (UIImage *)image: (int) x :(int) y {

CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
const UInt8* data = CFDataGetBytePtr(pixelData);

int pixelInfo = ((image.size.width  * y) + x ) * 4; // The image is png

//UInt8 red = data[pixelInfo];         // If you need this info, enable it
//UInt8 green = data[(pixelInfo + 1)]; // If you need this info, enable it
//UInt8 blue = data[pixelInfo + 2];    // If you need this info, enable it
UInt8 alpha = data[pixelInfo + 3];     // I need only this info for my maze game
CFRelease(pixelData);

//UIColor* color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f]; // The pixel color info

if (alpha) return YES;
else return NO;
}