2009-06-25 3 views
7

バイトバッファ(0〜255)を浮動バッファ(0.0〜1.0)に変換するにはどうすればよいですか?もちろん、2つの値の間には関係があるはずです。例えば、バイトバッファの0はfloatバッファの.0.f、バイトバッファの128はfloatバッファの.5f、バイトバッファの255は1.fになりますフロートバッファー。BYTEバッファ(0-255)を浮動バッファ(0.0-1.0)に変換する

実はこれは私が持っているコードです:

for (int y=0;y<height;y++) { 
    for (int x=0;x<width;x++) { 
     float* floatpixel = floatbuffer + (y * width + x) * 4; 
     BYTE* bytepixel = (bytebuffer + (y * width + x) * 4); 
     floatpixel[0] = bytepixel[0]/255.f; 
     floatpixel[1] = bytepixel[1]/255.f; 
     floatpixel[2] = bytepixel[2]/255.f; 
     floatpixel[3] = 1.0f; // A 
    } 
} 

これは非常に遅い実行されます。私の友人が私に変換テーブルを使用するように勧めましたが、他の人が私に別のアプローチを与えることができるかどうかを知りたかったのです。

+1

完全性のために、バイトバッファ内の128は浮動小数点バッファ内の.5019607843fであり、.5fではありません。 –

答えて

9

ルックアップテーブルを使用するかどうかにかかわらず、コードでは実際には必要のないループごとの繰り返しを実行しています。変換して乗算するコストを隠すのに十分なほどです。

あなたのポインタを制限し、constから読み込むだけのポインタを宣言してください。 255で割る代わりに1/255thを掛けます。内部ループの各反復でポインタを計算せず、初期値を計算してインクリメントします。内部ループを数回展開します。ターゲットがサポートしている場合は、ベクトルSIMD演算を使用してください。インクリメントして最大値と比較しないでください。代わりにゼロと比較してください。

float* restrict floatpixel = floatbuffer; 
BYTE const* restrict bytepixel = bytebuffer; 
for(int size = width*height; size > 0; --size) 
{ 
    floatpixel[0] = bytepixel[0]*(1.f/255.f); 
    floatpixel[1] = bytepixel[1]*(1.f/255.f); 
    floatpixel[2] = bytepixel[2]*(1.f/255.f); 
    floatpixel[3] = 1.0f; // A 
    floatpixel += 4; 
    bytepixel += 4; 
} 

よう

何かが開始となります。

+1

いくつかの非常に良い提案。しかし、彼らはルックアップテーブルを打つことはありません。 ;-) –

+1

アーキテクチャによって異なります。 Multiplyとconvertは、ロードよりも安価です。特に、アーキテクチャのSIMD機能(MMX、SSE、Altivecなど)を使用して、単一の命令でピクセル全体で行うことができる場合は、しかし、その決定は上記のすべての提案とは独立して行うことができます。 – moonshadow

+0

これは実際に速度を向上させるよりも、コンパイラの仕事を簡単にするために多くのことを行います。ポインタを整列させてSIMDを有効にすることを除いて - 実際のブーストを与えることができます – ima

2

このためにスタティックルックアップテーブルを使用します。私がコンピュータグラフィックス会社で働いていたとき、私たちはプロジェクトにリンクしたハードコードされたルックアップテーブルを持っていました。

1

はい、ルックアップテーブルは、ループ内で多くの分割を行うよりもはるかに高速です。 256の事前計算された浮動小数点値のテーブルを生成し、バイト値を使用してそのテーブルのインデックスを作成するだけです。

あなたはまた少し指数計算を除去することにより、ループを最適化し、ちょうどあなたがボトルネックが何であるかを確認する必要があり

float *floatpixel = floatbuffer; 
BYTE *bytepixel = bytebuffer; 

for (...) { 
    *floatpixel++ = float_table[*bytepixel++]; 
    *floatpixel++ = float_table[*bytepixel++]; 
    *floatpixel++ = float_table[*bytepixel++]; 
    *floatpixel++ = 1.0f; 
} 
2

のような何かを行うことができます:

  • あなたのデータが反復た場合テーブルが「間違った」方向にあると、常にキャッシュミスが発生します。ルックアップはこれを回避するのに役立ちます。
  • プロセッサのスケーリングがルックアップよりも遅い場合は、ルックアップテーブルがキャッシュに合っていれば、ルックアップによってパフォーマンスを向上させることができます。

もう一つのヒント:

struct Scale { 
    BYTE operator()(const float f) const { return f * 1./255; } 
}; 
std::transform(float_table, float_table + itssize, floatpixel, Scale()); 
0

は1/255を毎回計算しないでください。コンパイラがこれを削除するのに十分なほどスマートになるかどうかは分かりません。それを一度計算し、毎回それを再適用してください。さらに良く、定数として定義してください。

+0

コンパイラは定数フォールディングを実行するので、これは問題ではありません。 –

1

ルックアップテーブルは、変換する最速の方法です:)ここに行く:

Pythonコードをbyte_to_floatを生成します。

#!/usr/bin/env python 

def main(): 
    print "static const float byte_to_float[] = {" 

    for ii in range(0, 255): 
     print "%sf," % (ii/255.0) 

    print "1.0f };"  
    return 0 

if __name__ == "__main__": 
    main() 

をとC++のコードでは、変換を取得する:Hファイルが含まれるように

floatpixel[0] = byte_to_float[ bytepixel[0] ]; 

簡単なことではありませんの?

8

これは古い質問ですが、だれもIEEE float表現を使用して解決策を提示していないので、ここに1つあります。

// Use three unions instead of one to avoid pipeline stalls 
union { float f; uint32_t i; } t, u, v, w; 
t.f = 32768.0f; 
float const b = 256.f/255.f; 

for(int size = width * height; size > 0; --size) 
{ 
    u.i = t.i | bytepixel[0]; floatpixel[0] = (u.f - t.f) * b; 
    v.i = t.i | bytepixel[1]; floatpixel[1] = (v.f - t.f) * b; 
    w.i = t.i | bytepixel[2]; floatpixel[2] = (w.f - t.f) * b; 
    floatpixel[3] = 1.0f; // A 
    floatpixel += 4; 
    bytepixel += 4; 
} 

これは私のコンピュータ上のintfloatへの変換(Core 2 DuoプロセッサのCPU)など倍の速度を超えるです。

ここでは、一度に16個の浮動小数点を処理する上記のコードのSSE3バージョンを示します。 bytepixelfloatpixelは128ビットに整列し、合計サイズは4の倍数にする必要があります。SSE3の組み込みint型浮動小数点変換は、とにかく追加の乗算を必要とするため、ここではあまり役に立ちません。私はこれが命令的に行くための最短の方法だと考えていますが、あなたのコンパイラが十分に巧妙でない場合は、手作業で展開しスケジュールを設定することをお勧めします。

/* Magic values */ 
__m128i zero = _mm_set_epi32(0, 0, 0, 0); 
__m128i magic1 = _mm_set_epi32(0xff000000, 0xff000000, 0xff000000, 0xff000000); 
__m128i magic2 = _mm_set_epi32(0x47004700, 0x47004700, 0x47004700, 0x47004700); 
__m128 magic3 = _mm_set_ps(32768.0f, 32768.0f, 32768.0f, 32768.0f); 
__m128 magic4 = _mm_set_ps(256.0f/255.0f, 256.0f/255.0f, 256.0f/255.0f, 256.0f/255.0f); 

for(int size = width * height/4; size > 0; --size) 
{ 
    /* Load bytes in vector and force alpha value to 255 so that 
    * the output will be 1.0f as expected. */ 
    __m128i in = _mm_load_si128((__m128i *)bytepixel); 
    in = _mm_or_si128(in, magic1); 

    /* Shuffle bytes into four ints ORed with 32768.0f and cast 
    * to float (the cast is free). */ 
    __m128i tmplo = _mm_unpacklo_epi8(in, zero); 
    __m128i tmphi = _mm_unpackhi_epi8(in, zero); 
    __m128 in1 = _mm_castsi128_ps(_mm_unpacklo_epi16(tmplo, magic2)); 
    __m128 in2 = _mm_castsi128_ps(_mm_unpackhi_epi16(tmplo, magic2)); 
    __m128 in3 = _mm_castsi128_ps(_mm_unpacklo_epi16(tmphi, magic2)); 
    __m128 in4 = _mm_castsi128_ps(_mm_unpackhi_epi16(tmphi, magic2)); 

    /* Subtract 32768.0f and multiply by 256.0f/255.0f */ 
    __m128 out1 = _mm_mul_ps(_mm_sub_ps(in1, magic3), magic4); 
    __m128 out2 = _mm_mul_ps(_mm_sub_ps(in2, magic3), magic4); 
    __m128 out3 = _mm_mul_ps(_mm_sub_ps(in3, magic3), magic4); 
    __m128 out4 = _mm_mul_ps(_mm_sub_ps(in4, magic3), magic4); 

    /* Store 16 floats */ 
    _mm_store_ps(floatpixel, out1); 
    _mm_store_ps(floatpixel + 4, out2); 
    _mm_store_ps(floatpixel + 8, out3); 
    _mm_store_ps(floatpixel + 12, out4); 

    floatpixel += 16; 
    bytepixel += 16; 
} 

編集(f + c/b) * b代わりにf * b + cを使用して精度を向上させます。

:SSE3バージョンを追加します。

+0

SSE組み込み関数を使用してこれを行うことはできませんか?これは、SIMDコードの古典的な例のようです。 (もちろん元のコードでも同じです...) –

+0

はい! SSEはシャッフル機能が限定されていますが、ここでは便利です。 –