2012-05-01 134 views
11

關於如何獲取給定點的圖像像素顏色,有許多問題/答案。但是,對於大圖像(例如,甚至小至1000 x 1300),所有這些答案都是真的慢(100-500ms)。iOS性能調整:獲取大圖像像素顏色的最快方法

大部分代碼樣本都繪製到圖像上下文中。他們都需要時間,當實際平局發生:

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

檢查這在儀器顯示,平局是被複制從原始圖像數據完成:

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

現在,這種方法在一些非正式測試中表現得更好,但它仍然不如我想要的那麼快。 有誰知道獲取底層像素數據的更快方法?

回答

1

CGImage上下文可能幾乎是空的,並且不包含實際的像素數據,除非您嘗試讀取第一個像素或繪製它,因此嘗試加快從圖像獲取像素可能無法讓您獲得任何位置。沒有什麼可以得到。

你想讀取PNG文件中的像素嗎?您可以嘗試直接執行文件並對其進行mmap處理並自行解碼PNG格式。從存儲中獲取數據仍然需要一段時間。

+0

該圖像已被繪製在屏幕上,我想它的像素數據已經在內存中的某處。 –

+0

圖形/ GPU /顯示內存不同於CPU內存。您無法讀取顯示屏的內存。這是不透明的,在應用程序的沙箱外。 – hotpaw2

6

如果您可以通過OpenGL ES將此圖像繪製到屏幕上,則可以通過該版本中引入的紋理緩存對iOS 5.0中的底層像素進行非常快速的隨機訪問。它們允許直接訪問存儲在OpenGL ES紋理(圖像所在的位置)中的底層BGRA像素數據,並且您幾乎可以瞬間從該紋理中挑選出任何像素。

我用它讀取偶數大(2048x2048)圖像的原始像素數據,並且讀取時間在10-20毫秒範圍內最差,以便拉低所有像素。同樣,隨機訪問單個像素幾乎不需要時間,因爲您只是從字節數組中的位置讀取數據。

當然,這意味着你必須解析並上傳你的特定圖像到OpenGL ES,這將涉及到與磁盤相同的讀取和與核心圖形的交互(如果通過UIImage),你會看到如果您嘗試從磁盤上的隨機PNG中讀取像素數據,但聽起來您只需渲染一次並從中多次採樣。如果是這樣的話,OpenGL ES和iOS 5.0上的紋理緩存將是讀取這些像素數據以獲得屏幕上顯示內容的絕對最快的方式。

如果你想看看這樣的工作是否可行,我將這些進程封裝在我的開源GPUImage框架中的GPUImagePicture(圖片上傳)和GPUImageRawData(快速原始數據訪問)類中。

-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; 
} 
+0

請提供更多解釋給你的答案。 – zachjs

+0

謝謝,我不得不改變聲明來讀取'( - (BOOL)isWallPixel:(UIImage *)image theX:(int)x theY:(int)y {' –

+0

-1:這只是這個答案的副本](http://stackoverflow.com/a/7101544/400056)沒有歸屬。 – DarkDust

4

我還沒有找到一種方法來訪問繪製的(在幀緩衝區)像素。我測量過的最快方法是:

  1. 指示您希望在創建圖像時指定kCGImageSourceShouldCache來緩存圖像。
  2. (可選)通過強制渲染來預覽圖像。
  3. 在1x1位圖上下文中繪製圖像。

此方法的成本是緩存的位圖,只要與其關聯的CGImage可能具有一定的生命週期。該代碼最終看起來是這樣的:

  1. 創建圖像W/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); 
    

pixelData具有testPoint像素的R,G,B和A值。

相關問題