Pergunta

Como posso converter um buffer BYTE (de 0 a 255) para um buffer float (0,0-1,0)? É claro que deve haver uma relação entre os dois valores, por exemplo: 0 no byte de tampão será .0.f em tampão flutuador, 128 em tampão de byte será .5f em tampão flutuador, 255 em tampão de byte será em 1.f flutuador tampão.

Na verdade, este é o código que eu tenho:

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

Este é executado muito lento. Um amigo meu sugeriu que eu use uma tabela de conversão, mas eu queria saber se alguém pode me dar uma outra abordagem.

Foi útil?

Solução

Se você optar por usar uma tabela de pesquisa ou não, o seu código está fazendo um monte de trabalho a cada iteração do loop que ele realmente não precisa -. Bastante provável a ofuscar o custo da conversão e multiplicar

Declare seus ponteiros restringir, e ponteiros você apenas lê a partir de const. Multiplique por 1/255 em vez de dividir por 255. Não calcule os ponteiros em cada iteração do loop interno, basta calcular os valores iniciais e incrementar-los. Desenrolar o loop interno algumas vezes. Use vector SIMD operações se seus suportes alvejá-lo. Não incremento e comparar com a máxima, decremento e comparar com zero em vez.

Algo como

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

seria um começo.

Outras dicas

Eu sei que isto é uma questão de idade, mas já que ninguém deu uma solução usando a representação IEEE float, aqui é um deles.

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

Este é mais do que duas vezes mais rápido como um int à conversão float no meu computador (CPU Core 2 Duo).

Aqui está uma versão SSE3 do código acima que faz 16 carros alegóricos de cada vez. Ela exige bytepixel e floatpixel a ser de 128 bits alinhados, eo tamanho total para ser um múltiplo de 4. Note que o SSE3 embutido int para flutuar conversões não vai ajudar muito aqui, como eles vão exigir uma multiplicação adicional de qualquer maneira. Eu acredito que este é o caminho mais curto para ir instrução-sábio, mas se o seu compilador não é suficiente inteligente você pode querer as coisas passarem e agendar manualmente.

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

Editar :. Melhorar a precisão usando (f + c/b) * b vez de f * b + c

Editar : adicione versão SSE3

.

Use uma tabela de referência estática para isso. Quando eu trabalhava em uma empresa de computação gráfica, acabamos por ter um disco tabela de pesquisa codificado para isso que nós interligadas com o projeto.

Você precisa descobrir o que o gargalo é:

  • se tabelas que você iterate seus dados no sentido 'errado', você constantemente bater um cache miss. Sem pesquisa será sempre ajudar a obter em torno disso.
  • se o seu processador é mais lenta em escala do que em olhar para cima, você pode aumentar o desempenho, observando-se, desde os de tabela de pesquisa encaixa-lo do cache.

Outra dica:

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

Sim, uma tabela de pesquisa é definitivamente mais rápido do que fazer um monte de divisões em um loop. Basta gerar uma tabela de 256 valores float pré-computadas e usar o valor de byte para indexar nessa tabela.

Você também pode otimizar o loop um pouco, removendo o cálculo do índice e apenas fazer algo como

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

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

tabela look-up é a maneira mais rápida para converter :) Aqui vai:

código Python para gerar o arquivo byte_to_float.h para incluir:

#!/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()

E código C ++ para obter a conversão:

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

Simples não é?

Não calcule 1/255 de cada vez. Não sei se um compilador será inteligente o suficiente para remover este. Calculá-lo uma vez e reaplicá-lo a cada vez. Mesmo melhor, defini-lo como uma constante.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top