7

如何将 BYTE 缓冲区(从 0 到 255)转换为浮点缓冲区(从 0.0 到 1.0)?当然这两个值之间应该有关系,例如:字节缓冲区中的0将是浮动缓冲区中的.0.f,字节缓冲区中的128将是浮动缓冲区中的0.5f,字节缓冲区中的255将是1.f in浮动缓冲区。

实际上这是我拥有的代码:

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
    }
}

这运行非常缓慢。我的一个朋友建议我使用转换表,但我想知道其他人是否可以给我另一种方法。

4

7 回答 7

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

将是一个开始。

于 2009-06-25T13:13:59.397 回答
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;
}

这比我的计算机(Core 2 Duo CPU)上的转换速度快两倍多。intfloat

这是上述代码的 SSE3 版本,一次执行 16 个浮点数。它需要128 位对齐,bytepixel并且floatpixel总大小是 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 版本。

于 2011-03-19T14:46:57.047 回答
2

为此使用静态查找表。当我在一家计算机图形公司工作时,我们最终获得了一个硬编码的查找表,我们将其与项目相关联。

于 2009-06-25T13:12:05.487 回答
2

您需要找出瓶颈是什么:

  • 如果您以“错误”的方向迭代数据表,您会经常遇到缓存未命中。没有查找将有助于解决这个问题。
  • 如果您的处理器的扩展速度比查找速度慢,您可以通过查找来提高性能,前提是查找表适合它的缓存。

另一个提示:

struct Scale {
    BYTE operator()( const float f ) const { return f * 1./255; }
};
std::transform( float_table, float_table + itssize, floatpixel, Scale() );
于 2009-06-25T13:19:16.127 回答
1

是的,查找表肯定比在循环中进行大量除法要快。只需生成一个包含 256 个预先计算的浮点值的表,并使用字节值来索引该表。

您还可以通过删除索引计算来稍微优化循环,然后执行类似的操作

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

for (...) {
  *floatpixel++ = float_table[*bytepixel++];
  *floatpixel++ = float_table[*bytepixel++];
  *floatpixel++ = float_table[*bytepixel++];
  *floatpixel++ = 1.0f;
}
于 2009-06-25T13:14:41.300 回答
1

查找表是最快的转换方式:) 给你:

生成 byte_to_float.h 文件的 Python 代码包括:

#!/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] ];

是不是很简单?

于 2010-03-01T11:22:47.403 回答
0

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

于 2009-06-26T05:28:58.450 回答