10

画像の平均R、G、B値を計算するためにこのメソッドを書いています。次のメソッドは、UIImageを入力として受け取り、入力画像のR、G、B値を含む配列を返します。ただし、質問が1つあります。CGImageRefを適切にリリースするにはどうすればよいですか。

-(NSArray *)getAverageRGBValuesFromImage:(UIImage *)image
{
    CGImageRef rawImageRef = [image CGImage];

    //This function returns the raw pixel values
    const UInt8 *rawPixelData = CFDataGetBytePtr(CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef)));

    NSUInteger imageHeight = CGImageGetHeight(rawImageRef);
    NSUInteger imageWidth = CGImageGetWidth(rawImageRef);

    //Here I sort the R,G,B, values and get the average over the whole image
    int i = 0;
    unsigned int red = 0;
    unsigned int green = 0;
    unsigned int blue = 0;

    for (int column = 0; column< imageWidth; column++)
    {
        int r_temp = 0;
        int g_temp = 0;
        int b_temp = 0;

        for (int row = 0; row < imageHeight; row++) {
            i = (row * imageWidth + column)*4;
            r_temp += (unsigned int)rawPixelData[i];
            g_temp += (unsigned int)rawPixelData[i+1];
            b_temp += (unsigned int)rawPixelData[i+2];

        }

        red += r_temp;
        green += g_temp;
        blue += b_temp;

    }

    NSNumber *averageRed = [NSNumber numberWithFloat:(1.0*red)/(imageHeight*imageWidth)];
    NSNumber *averageGreen = [NSNumber numberWithFloat:(1.0*green)/(imageHeight*imageWidth)];
    NSNumber *averageBlue = [NSNumber numberWithFloat:(1.0*blue)/(imageHeight*imageWidth)];


    //Then I store the result in an array
    NSArray *result = [NSArray arrayWithObjects:averageRed,averageGreen,averageBlue, nil];


    return result;
}

2つのことを試しました。オプション1:そのままにしておきますが、数サイクル(5+)後にプログラムがクラッシュし、「メモリ不足の警告エラー」が発生します。

オプション2:メソッドが戻る前に1行のCGImageRelease(rawImageRef)を追加します。2番目のサイクルの後にクラッシュするようになりました。メソッドに渡すUIImageに対してEXC_BAD_ACCESSエラーが発生します。Xcodeで(RUNではなく)分析しようとすると、この行に次の警告が表示されます。「この時点で呼び出し元が所有していないオブジェクトの参照カウントの誤ったデクリメント」

CGImageRefをどこでどのようにリリースする必要がありますか?

ありがとう!

4

5 に答える 5

68

他の人が述べているように、あなたのメモリの問題はコピーされたデータに起因します。しかし、ここに別のアイデアがあります。コアグラフィックスの最適化されたピクセル補間を使用して平均を計算します。

  1. 1x1ビットマップコンテキストを作成します。
  2. 補間品質を中程度に設定します(後述)。
  3. 正確にこの1ピクセルに縮小された画像を描画します。
  4. コンテキストのバッファからRGB値を読み取ります。
  5. (もちろん、コンテキストを解放します。)

Core Graphicsは高度に最適化されており、ダウンスケーリングにGPUを使用する場合もあるため、これによりパフォーマンスが向上する可能性があります。

テストでは、中程度の品質が色の値の平均を取ることによってピクセルを補間しているように見えることが示されました。それが私たちがここで望んでいることです。

少なくとも試してみる価値はあります。

編集:わかりました、このアイデアは試してはいけないほど面白そうだった。違いを示すプロジェクトの例を次に示します。以下の測定は、含まれている512x512のテスト画像を使用して行われましたが、必要に応じて画像を変更できます。

画像データのすべてのピクセルを反復処理して平均を計算するには、約12.2ミリ秒かかります。1ピクセルに描画するアプローチは3ミリ秒かかるため、約4倍高速です。を使用しても同じ結果が得られるようkCGInterpolationQualityMediumです。

パフォーマンスが大幅に向上したのは、QuartzがJPEGを完全に解凍する必要はないが、DCTの低周波数部分のみを使用できることに気付いた結果だと思います。これは、スケールが0.5未満のJPEG圧縮ピクセルを作成する場合の興味深い最適化戦略です。しかし、私はここで推測しているだけです。

興味深いことに、この方法を使用すると、時間の70%がCGDataProviderCopyDataピクセルデータの走査に費やされ、30%だけが費やされます。これは、JPEG解凍に多くの時間を費やしたことを示唆しています。

ピクセル反復スクリーンショット 1ピクセルに描画するスクリーンショット

注:上の画像の例については、後からフォローアップします。

于 2012-08-27T19:14:49.683 に答える
8

CGImageRef rawImageRefを使用して取得したため、 を所有していません [image CGImage]。したがって、リリースする必要はありません。

ただし、rawPixelData使用して取得したため、所有しCGDataProviderCopyData、解放する必要があります。

CGDataProviderCopyData

戻り値: プロバイダーのデータのコピーを含む新しいデータ オブジェクト。このオブジェクトを解放する責任があります。

于 2012-08-27T18:54:23.983 に答える
4

あなたの問題は次の声明にあると思います。

const UInt8 *rawPixelData = CFDataGetBytePtr(CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef)));

CGDataProviderCopyData の戻り値を解放する必要があります。

于 2012-08-27T18:57:25.370 に答える
4

mergedColor は、ファイルから読み込まれた画像ではうまく機能しますが、カメラによる画像キャプチャでは機能しません。CGBitmapContextGetData()キャプチャされたサンプル バッファから作成されたコンテキストでは、ビットマップが返されないためです。コードを次のように変更しました。どんな画像でも機能し、コードと同じくらい高速です。

- (UIColor *)mergedColor
{
     CGImageRef rawImageRef = [self CGImage];

    // scale image to an one pixel image

    uint8_t  bitmapData[4];
    int bitmapByteCount;
    int bitmapBytesPerRow;
    int width = 1;
    int height = 1;

    bitmapBytesPerRow = (width * 4);
    bitmapByteCount = (bitmapBytesPerRow * height);
    memset(bitmapData, 0, bitmapByteCount);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,
             colorspace,kCGBitmapByteOrder32Little|kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(colorspace);
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGContextSetInterpolationQuality(context, kCGInterpolationMedium);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), rawImageRef);
    CGContextRelease(context);
    return [UIColor colorWithRed:bitmapData[2] / 255.0f
                    green:bitmapData[1] / 255.0f
                             blue:bitmapData[0] / 255.0f
                            alpha:1];
}
于 2012-09-27T04:17:19.367 に答える
3
CFDataRef abgrData = CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef));

const UInt8 *rawPixelData = CFDataGetBytePtr(abgrData);

...

CFRelease(abgrData);
于 2012-08-27T18:55:28.477 に答える