将BYTE缓冲区(0-255)转换为浮点缓冲区(0.0-1.0)

问题描述:

如何将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

无论您是否选择使用查找表,您的代码在每次循环迭代过程中都会执行大量工作,而这实际上并不需要 - 可能足以掩盖转换和乘法的成本。

声明你的指针限制,指针只能从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

对此使用静态查找表。当我在一家计算机图形公司工作时,我们最终得到了一个硬编码的查找表,用于与项目链接。

是的,查找表肯定比在循环中做很多分区更快。只需生成256个预先计算的浮点值表并使用该字节值来索引该表。

你也可以一点点去除指数计算优化的循环,只是这样做

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

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

你需要找出瓶颈是什么:

  • ,如果你重复数据在'错误'方向的表格中,你总是碰到缓存未命中。没有查找将有助于解决这个问题。
  • 如果您的处理器缩放比查找速度慢,您可以通过查找来提高性能,前提是查找表适合缓存。

另一个秘诀:

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

不计算每次1/255。不知道编译器是否足够聪明,可以删除它。计算一次,每次重新应用它。更好的是,将其定义为一个常量。

查找表转换:)在这里你走的最快方法:

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

简单,不是吗?

我知道这是一个古老的问题,但由于没有人给出了使用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版本。