2009-06-25 99 views
7

如何將BYTE緩衝區(從0到255)轉換爲浮點緩衝區(從0.0到1.0)?當然,這兩個值之間應該有一個關係,例如:字節緩衝區中的0將是浮點緩衝區中的.0.f,緩衝區中的128個字節將是.5f,浮點緩衝區中是.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。 – 2011-03-19 13:32:00

回答

9

無論您是否選擇使用查找表,您的代碼在每次循環迭代過程中都會執行大量工作,而這實際上並不需要 - 可能足以掩蓋轉換和乘法的成本。

聲明你的指針限制,指針只能從const讀取。乘以1/255而不是除以255.不要在內部循環的每次迭代中計算指針,只需計算初始值並將其增加。展開內部循環幾次。如果您的目標支持它,則使用矢量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

一些非常好的建議。但他們不會打敗查找表。 ;-) – 2009-06-25 13:15:37

+1

取決於架構。乘法和轉換可能比加載更便宜,尤其是如果他可以在單個指令中使用其架構的SIMD功能(MMX,SSE,Altivec或其他)來完成整個像素。但是,這個決定可以獨立於上述所有建議。 – moonshadow 2009-06-25 13:25:37

+0

這對於編譯器的工作比實際提高速度更容易。除了對齊指針和啓用SIMD - 它可以給一個真正的提升 – ima 2009-06-25 13:30:24

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。不知道編譯器是否足夠聰明,可以刪除它。計算一次,每次重新應用它。更好的是,將其定義爲一個常量。

1

查找錶轉換:)在這裏你走的最快方法:

Python代碼生成byte_to_float。.h文件包括:

#!/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++代碼來獲取轉換:

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

簡單,不是嗎?

8

我知道這是一個古老的問題,但由於沒有人給出了使用IEEE浮點表示的解決方案,下面是一個。

// 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轉換我的計算機(酷睿2 CPU)上。

下面是上述代碼的SSE3版本,每次都執行16次浮動操作。它需要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版本。