2011-02-07 209 views
0

我正在開發一個圖像處理應用程序,我在尋找一個建議來調整我的代碼。iPhone SDK - 優化for循環

我的需求是將圖像分成塊(80x80),並且對於每個塊,計算平均顏色。

我的第一種方法包含在第二種方法被稱爲主循環:

- (NSArray*)getRGBAsFromImage:(UIImage *)image { 
int width  = image.size.width; 
int height = image.size.height; 

int blocPerRow = 80; 
int blocPerCol = 80; 

int pixelPerRowBloc = width/blocPerRow; 
int pixelPerColBloc = height/blocPerCol; 

int xx,yy; 

// Row loop 
for (int i=0; i<blocPerRow; i++) { 

    xx = (i * pixelPerRowBloc) + 1; 

    // Colon loop 
    for (int j=0; j<blocPerCol; j++) { 

     yy = (j * pixelPerColBloc) +1; 

     [self getRGBAsFromImageBloc:image 
        atX:xx 
        andY:yy 
        withPixelPerRow:pixelPerRowBloc 
        AndPixelPerCol:pixelPerColBloc]; 
    } 
} 
// return my NSArray not done yet ! 
} 

我的第二個方法瀏覽像素集團並返回一個ColorStruct:

- (ColorStruct*)getRGBAsFromImageBloc:(UIImage*)image 
          atX:(int)xx 
          andY:(int)yy 
          withPixelPerRow:(int)pixelPerRow 
          AndPixelPerCol:(int)pixelPerCol { 

// First get the image into your data buffer 
CGImageRef imageRef = [image CGImage]; 

NSUInteger width = CGImageGetWidth(imageRef); 
NSUInteger height = CGImageGetHeight(imageRef); 

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

unsigned char *rawData = malloc(height * width * 4); 

NSUInteger bytesPerPixel = 4; 
NSUInteger bytesPerRow = bytesPerPixel * width; 

    NSUInteger bitsPerComponent = 8; 

CGContextRef context = CGBitmapContextCreate(rawData, width, height, 
       bitsPerComponent, bytesPerRow, colorSpace, 
       kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); 

CGColorSpaceRelease(colorSpace); 

CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); 
CGContextRelease(context); 

// Now your rawData contains the image data in the RGBA8888 pixel format. 
int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel; 

    int red = 0; 
    int green = 0; 
    int blue = 0; 
    int alpha = 0; 
    int currentAlpha; 

    // bloc loop 
    for (int i = 0 ; i < (pixelPerRow*pixelPerCol) ; ++i) { 
     currentAlpha = rawData[byteIndex + 3]; 

     red += (rawData[byteIndex]  ) * currentAlpha; 
     green += (rawData[byteIndex + 1]) * currentAlpha; 
     blue += (rawData[byteIndex + 2]) * currentAlpha; 
     alpha += currentAlpha; 

     byteIndex += 4; 

     if (i == pixelPerRow) { 
      byteIndex += (width-pixelPerRow) * 4; 
     } 
    } 
    red  /= alpha; 
    green /= alpha; 
    blue /= alpha; 

    ColorStruct *bColorStruct = newColorStruct(red, blue, green); 

    free(rawData); 

    return bColorStruct; 
    } 

ColorStruct:

typedef struct { 
    int red; 
    int blue; 
    int green; 
} ColorStruct; 

帶構造函數:

ColorStruct *newColorStruct(int red, int blue, int green) { 
ColorStruct *ret = malloc(sizeof(ColorStruct)); 
ret->red = red; 
    ret->blue = blue; 
ret->green = green; 
return ret; 
} 

正如你所看到的,我有三個級別的循環:行循環,冒號循環和集合循環。

我測試了我的代碼,320x480圖片需要大約5到6秒。

歡迎任何幫助。

感謝, Bahaaldine

回答

2

似乎給它的大中央調度一個完美的問題?

0

您正在檢查每一個像素 - 看起來,無論您如何循環,都會花費大致相同的時間(假設您只檢查一次像素)。

我會建議在區塊內使用隨機採樣 - 每個「n」個像素,這會減少循環時間(和精度),或者允許可調節粒度。

現在,如果存在用於計算一組像素的平均值的現有算法 - 那麼可以考慮將其作爲替代方案。

0

您可以通過在循環中不調用方法來加快速度。只需內聯代碼即可。

ADDED:另外,如果您有足夠的內存,您可以嘗試只繪製一次繪製圖像,而不是循環重複。

當你這樣做後,你可以嘗試從內部循環中提取一些乘法以及一些額外的性能(儘管編譯器可能會爲你優化一些)。

1

我認爲這個代碼的主要問題是有太多的圖像讀取。整個圖像被加載到每個(!)塊的內存中(malloc也很昂貴)。您應該預先加載一次圖像數據(緩存),然後在getRGBAsFromImageBloc()中使用該內存。現在對於320x480的圖片,您有4 x 6 = 24塊。所以你可以通過只使用緩存來加速你的應用程序。

1

在一天結束時拍攝一張圖像,並對每個像素依次執行三次乘法和五次加法總是會相對較慢。

幸運的是,您正在做的事情可以被看作是將圖像從一個尺寸插值到另一個尺寸的特殊情況 - 即圖像的平均像素與調整大小爲1x1的圖像的平均像素相同(假設調整大小是使用某種形式的線性插值,但這通常是標準方法),並且有一些高度優化(或者至少比沒有大量努力可能獲得更多的優化)選項可以做到這一點的iPhone圖形庫。起初,我嘗試使用石英的方法來調整圖像大小:

CGImageRef sourceImage = yourImage; 

int numBytesPerPixel = 4; 
u_char* scaledImageData = (u_char*)malloc(numBytesPerPixel); 

CGColorSpaceRef colorspace = CGImageGetColorSpace(sourceImage); 
CGContextRef context = CGBitmapContextCreate (scaledImageData, 1, 1, 8, numBytesPerPixel, colorspace, kCGImageAlphaNoneSkipFirst); 
CGColorSpaceRelease(colorspace); 
CGContextDrawImage(context, CGRectMake(0,0,1,1), sourceImage); 

int a = scaledImageData[0]; 
int r = scaledImageData[1]; 
int g = scaledImageData[2]; 
int b = scaledImageData[3]; 

(這只是縮放原始圖像降低到1個像素,並且不顯示其子區域的裁剪但不幸的是我沒有現在有時間爲該代碼 - 如果你嘗試實現它,並卡住添加評論,我可以告訴你如何做到這一點)。

如果這不起作用,您可以嘗試使用OpenGL ES來執行此操作(從需要縮放的圖像部分創建紋理,將其渲染到1x1緩衝區,然後測試緩衝區的結果)。這是一個複雜得多,但可能有一些優勢,因爲它使您可以訪問GPU,這可能是大圖像快很多。

希望是有道理的,並幫助...

P.S. - 絕對遵循y0prst的建議,只能一次讀取圖像 - 這是一個簡單的解決方案,將爲您帶來大量的性能。

P.P.S - 我還沒有測試代碼,所以通常的警告適用。