12

RGBA 画像データから 1 つまたは 2 つのカラー チャネルをできるだけ早くコピーしようとしています (これはコードの最も遅い部分であり、アプリ全体の速度が低下しています)。ストライドでコピーする高速な方法はありますか?

データは単純に RGBARGBARGBA などのように配置され、R 値だけをコピーするか、別のケースでは RG 値だけをコピーする必要があります。

私がこれまでに持っているのは、R値をコピーするための大まかな方法​​です:

for(int i=0; i<dataSize; i++){
    dest[i] = source[i*4];
}

RG 値については、次のようにしています。

for(int i=0; i<dataSize; i+=2){
    dest[i] = source[i*2];
    dest[i+1] = source[(i*2)+1];
}

データはすべて符号なしの 1 バイト値です。もっと速い方法はありますか?私はすでにループを部分的に展開しました (反復ごとに 64 の値を実行します - それ以上のスピードアップはわずかです)。プラットフォームは Armv7 (iOS) であるため、NEON (SIMD) を使用すると便利な場合がありますが、残念ながら経験がありません!

データを変更することは残念ながら問題外です。これは opengl の readPixels() 関数によって提供され、iOS は L、LA、RG などの読み取りを私が知る限りサポートしていません。

4

8 に答える 8

5

iOS4 以降に問題がなければ、vDSP と加速フレームワークが役立つかもしれません。ワープ速度でのあらゆる種類の画像操作の利点については、ドキュメントを確認してください。

#import <Accelerate/Accelerate.h>

次に何をするかはわかりませんが、画像データに対してなんらかの計算を行っていて、浮動小数点形式が必要な場合は、vDSP_vfltu8 を使用して、ソース バイト データの 1 つのチャネルを単精度浮動小数点に変換できます。このような単一の行を使用するポイント (メモリ管理を除く);

vDSP_vfltu8(srcData+0,4,destinationAsFloatRed,1,numberOfPixels)
vDSP_vfltu8(srcData+1,4,destinationAsFloatGreen,1,numberOfPixels)
vDSP_vfltu8(srcData+2,4,destinationAsFloatBlue,1,numberOfPixels)
vDSP_vfltu8(srcData+3,4,destinationAsFloatAlpha,1,numberOfPixels)

その後、操作された浮動小数点データからイメージを作成する必要がある場合は、vDSP_vfuxu8 を使用して逆に戻ります。

vDSP_vfixu8(destinationAsFloatRed,1,outputData+0,4,numberOfPixels);
vDSP_vfixu8(destinationAsFloatGreen,1,outputData+1,4,numberOfPixels);
vDSP_vfixu8(destinationAsFloatBlue,1,outputData+2,4,numberOfPixels);
vDSP_vfixu8(destinationAsFloatAlpha,1,outputData+3,4,numberOfPixels);

明らかに、上記の手法を使用して 1 つまたは 2 つのチャネルを処理できます。

ドキュメントは非常に複雑ですが、結果は良好です。

于 2011-06-27T08:31:42.337 に答える
3

いつものように、ロードとストアは最もコストのかかる操作です。次の方法でコードを最適化できます。

  • 1 つの int をロード (RGBA)
  • 必要な部分をレジスタに格納 (一時変数)
  • データを一時変数の正しい場所にシフトします。
  • ネイティブ プロセッサのデータ サイズがいっぱいになるまでこれを行います (32 ビット マシンの文字の場合は 4 回)。
  • 一時変数をメモリに保存します。

コードは、アイデアを理解するために高速で型付けされています。

unsigned int tmp;
unsigned int *dest;

for(int i=0; i<dataSize; i+=4){
    tmp  = (source[i] & 0xFF);
    tmp |= (source[i+1] & 0xFF) << 8;
    tmp |= (source[i+2] & 0xFF) << 16;
    tmp |= (source[i+3] & 0xFF) << 24;

    *dest++ = tmp;
}
于 2011-06-27T10:12:30.240 に答える
2

私はもっ​​とwhile男です-あなたはそれをに変換することができますfor、私は確信しています

i = j = 0;
while (dataSize--) {
    dst[i++] = src[j++]; /* R */
    dst[i++] = src[j++]; /* G */
    j += 2;              /* ignore B and A */
}

それが速いということに関しては、あなたは測定しなければなりません。

于 2011-06-27T08:25:13.820 に答える
2

コンパイルされたコードによっては、乗算を 2 で置き換えて、2 番目のループ インデックスを追加することができます (それjを呼び出して 4 進めます)。

for(int i=0, j=0; i<dataSize; i+=2, j+=4){
    dest[$i] = source[$j];
    dest[$i+1] = source[$j+1];
}

または、乗算を 1 のシフトに置き換えることができます。

for(int i=0, j=0; i<dataSize; i+=2, j+=4){
    dest[$i] = source[$i<<1];
    dest[$i+1] = source[($i<<1)+1];
}
于 2011-06-27T08:21:00.683 に答える
1

あなたの質問はまだ現実的ですか?数日前に、ストライドでバイトをコピーするための ASM アクセラレーション関数を公開しました。対応する C コードよりも約 2 倍高速です。ここで見つけることができます: https://github.com/noveogroup/ios-aux RG バイトのコピーの場合、単語をコピーするように変更できます。

UPD: コンパイラの最適化がデフォルトでオフになっている場合、デバッグ モードでのみ私のソリューションが C コードよりも高速であることを発見しました。リリース モードでは、C コードは (デフォルトで) 最適化され、ASM コードと同じくらい高速に動作します。

于 2013-09-05T10:33:58.600 に答える
1

パーティーに遅すぎないことを願っています!ARM NEON 組み込み関数を使用して、iPad で同様のことを実現しました。リストされている他の回答と比較して、2〜3倍の速度が向上します。以下のコードは最初のチャネルのみを保持し、データが 32 バイトの倍数である必要があることに注意してください。

uint32x4_t mask = vdupq_n_u32(0xFF);

for (unsigned int i=0, j=0; i < dataSize; i+=32, j+=8) {

    // Load eight 4-byte integers from the source
    uint32x4_t vec0 = vld1q_u32((const unsigned int *) &source[i]);
    uint32x4_t vec1 = vld1q_u32((const unsigned int *) &source[i+16]);

    // Zero everything but the first byte in each of the eight integers
    vec0 = vandq_u32(vec0, mask);
    vec1 = vandq_u32(vec1, mask);

    // Throw away two bytes for each of the original integers
    uint16x4_t vec0_s = vmovn_u32(vec0);
    uint16x4_t vec1_s = vmovn_u32(vec1);

    // Combine the remaining bytes into a single vector
    uint16x8_t vec01_s = vcombine_u16(vec0_s, vec1_s);

    // Throw away the last byte for each of the original integers
    uint8x8_t vec_o = vmovn_u16(vec01_s);

    // Store to destination
    vst1_u8(&dest[j], vec_o);
}
于 2014-07-31T00:31:54.997 に答える
1

ロジャーからの答えは、おそらく最もクリーンなソリューションです。コードを小さく保つためにライブラリを用意することは常に良いことです。しかし、C コードのみを最適化したい場合は、さまざまなことを試すことができます。まず、dataSize の大きさを分析する必要があります。次に、おそらくバイトではなく int をコピーすることと組み合わせて、重いループ展開を行うことができます: (疑似コード)

while(dataSize-i > n) { // n being 10 or whatever
   *(int*)(src+i) = *(int*)(dest+i); i++; // or i+=4; depending what you copy
   *(int*)(src+i) = *(int*)(dest+i);
   ... n times
}

残りは次のようにします。

switch(dataSize-i) {
    case n-1: *(src+i) = *(dest+i); i++;
    case n-2: ...
    case 1: ...
}

それは少し醜くなります..しかし、それは確かに速いです:)

dataSize がどのように動作するかを知っていれば、さらに最適化できます。多分それは常に2の累乗ですか?それとも偶数?


一度に4バイトをコピーすることはできません:)が、2バイトしかコピーできないことに気付きました。とにかく、展開されたループを switch ステートメントで 1 つの比較のみで終了する方法を示したかっただけです。IMOは、まともなスピードアップを得る唯一の方法です。

于 2011-06-27T08:50:37.147 に答える
0

ASM に慣れていますか? 私は ARM プロセッサに詳しくありませんが、Analog Devices の Blackfin では、計算操作と並行して実行できるため、このコピーは実際には無料です。

i0 = _src_addr;
i1 = _dest_addr;
p0 = dataSize - 1;

r0 = [i0++];
loop _mycopy lc0 = p0;
loop_begin _mycopy;
    /* possibly do compute work here | */ r0 = [i0++] | W [i1++] = r0.l;
loop_end _mycopy;
W [i1++] = r0.l;

つまり、1 ピクセルあたり 1 サイクルです。そのままで、これは RG または BA コピーに適していることに注意してください。私が言ったように、私は ARM に精通しておらず、iOS についてまったく知らないので、ASM コードにアクセスできるかどうかもわかりませんが、そのような最適化を探してみてください。

于 2011-06-27T13:14:28.153 に答える