2011-02-07 9 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 ! 
} 

私の第2の方法は、ピクセルのブロックを閲覧および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; 

私の最初の方法は、第2メソッドが呼び出され、メインループを含んでいます

コンストラクタ付き:

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

ご覧のとおり、行ループ、コロンループ、およびブロックループという3つのレベルのループがあります。

私のコードをテストしたところ、320x480の画像で約5〜6秒かかります。

助けを歓迎します。

おかげで、 Bahaaldine

答えて

2

はそれをグランドセントラル派遣を与えるために完璧な問題のように見えますか?

0

すべての単一ピクセルを検査しています。どのピクセルを検査しているかは、どのピクセルを1回だけ調べても、どのようにループしてもほぼ同じ時間がかかることになります。

ループ時間(および精度)を低下させるブロックごと、または調整可能な細かさを可能にするブロックごとにランダムサンプリングを使用することをお勧めします。

ここで、ピクセルグループの平均を計算するための既存のアルゴリズムがある場合、これは代替として検討するものです。

0

ループの途中でメソッドを呼び出さないようにすれば、処理速度を上げることができます。コードをインラインにするだけです。

ADDED:十分なメモリがある場合は、ループで繰り返されるのではなく、描画イメージを1回だけ試してみることもできます。

これを実行した後、内側のループからいくつかの倍数を引き上げることもできます(ただし、コンパイラはこれの一部を最適化できます)。

1

私はこのコードの主な問題は、あまりにも多くの画像の読み取りがあると思います。イメージ全体が(!)ブロックごとにメモリにロードされます(mallocも高価です)。イメージデータを1回プリロードして(キャッシュして)、そのメモリをgetRGBAsFromImageBloc()に使用する必要があります。 320x480の画像の場合、4 x 6 = 24ブロックになります。だから、キャッシュを使うだけで、アプリを何倍も高速化できます。

1

画像を撮って、各ピクセルで3回の乗算と5回の加算を連続して実行することは、常に比較的遅くなるでしょう。

幸いなことに、あるサイズから別のサイズに画像を補間する特殊なケースとして考えることができます。つまり、画像の平均ピクセルは、画像を1x1サイズにリサイズしたものと同じですサイズ変更には線形補間を使用していますが、それは通常は標準的な方法です)、いくつかのオプションを高度に最適化したり、 iPhoneのグラフィックスライブラリのこれはちょうど1つのピクセルまで、元の画像を拡大縮小し、サブ領域のトリミングを示していないが、残念ながら、私はしません(

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]; 

:最初に私は画像のサイズを変更するクォーツ・メソッドを使用して試してみましたあなたがそれを実装しようとすると、コメントを追加して、あなたがそれをやる方法をあなたに示すことができます)。

これでうまくいかない場合は、OpenGL ESを使用していつでも試してみることができます(イメージの一部からテクスチャを作成し、1x1バッファにレンダリングし、バッファから結果をテストする必要があります) )。これははるかに複雑ですが、GPUにアクセスできるという利点があります。これは、大きな画像の方がはるかに高速です。

ホップが意味を成し、助けてくれることを望みます...

P.S. - 間違いなくy0prstの提案に従って一度だけ画像を読む - それはあなたに1トンのパフォーマンスを買う簡単な修正です。

P.P.S - コードをテストしていないので、通常の警告が適用されます。

関連する問題