Pregunta

¿Cómo puedo convertir un búfer BYTE (de 0 a 255) a un búfer flotante (de 0.0 a 1.0)? Por supuesto, debe haber una relación entre los dos valores, por ejemplo: 0 en el búfer de bytes será .0.f en el búfer de flotación, 128 en el búfer de bytes será .5f en el búfer de flotación, 255 en el búfer de bytes será 1.f en búfer flotante.

En realidad, este es el código que tengo:

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

Esto corre muy lento. Un amigo mío me sugirió usar una tabla de conversión, pero quería saber si alguien más puede darme otro enfoque.

¿Fue útil?

Solución

Ya sea que elija usar una tabla de búsqueda o no, su código está haciendo mucho trabajo en cada iteración del ciclo que realmente no necesita, lo suficientemente probable como para eclipsar el costo de la conversión y la multiplicación.

Declara restringir tus punteros y punteros que solo lees de const. Multiplique por 1 / 255th en lugar de dividir por 255. No calcule los punteros en cada iteración del bucle interno, solo calcule los valores iniciales e increméntelos. Desenrolle el bucle interno varias veces. Utilice operaciones vectoriales SIMD si su objetivo lo admite. No incremente y compare con el máximo, disminuya y compare con cero en su lugar.

Algo así

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

sería un comienzo.

Otros consejos

Sé que esta es una vieja pregunta, pero como nadie dio una solución usando la representación flotante IEEE, aquí hay una.

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

Esto es más de dos veces más rápido que una conversión de int a float en mi computadora (CPU Core 2 Duo).

Aquí hay una versión SSE3 del código anterior que hace 16 flotantes a la vez. Requiere que bytepixel y floatpixel estén alineados a 128 bits, y que el tamaño total sea un múltiplo de 4. Tenga en cuenta que el SSE3 integrado para las conversiones flotantes no ayuda mucho aquí, ya que requerirán una multiplicación adicional de todos modos. Creo que este es el camino más corto para seguir las instrucciones, pero si su compilador no es lo suficientemente inteligente, es posible que desee desenrollar y programar las cosas a mano.

/* 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 : mejore la precisión utilizando (f + c / b) * b en lugar de f * b + c .

Editar : agregue la versión SSE3.

Use una tabla de búsqueda estática para esto. Cuando trabajaba en una empresa de gráficos por computadora, terminamos teniendo una tabla de búsqueda codificada para esto que vinculamos con el proyecto.

Debe averiguar cuál es el cuello de botella:

  • si itera sus tablas de datos en la dirección 'incorrecta', constantemente golpea un error de caché. Ninguna búsqueda ayudará a evitar eso.
  • si su procesador es más lento en la escala que en la búsqueda, puede aumentar el rendimiento al buscar, siempre que la tabla de búsqueda se ajuste a su caché.

Otro consejo:

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

Sí, una tabla de búsqueda es definitivamente más rápida que hacer muchas divisiones en un bucle. Simplemente genere una tabla de 256 valores flotantes precalculados y use el valor de byte para indexar esa tabla.

También puede optimizar un poco el ciclo al eliminar el cálculo del índice y simplemente hacer algo como

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

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

La tabla de búsqueda es la forma más rápida de convertir :) Aquí tienes:

Código de Python para generar el archivo 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()

Y código C ++ para obtener la conversión:

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

Simple, ¿no es así?

No calcules 1/255 cada vez. No sé si un compilador será lo suficientemente inteligente como para eliminar esto. Calcúlelo una vez y vuelva a aplicarlo cada vez. Aún mejor, defínalo como una constante.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top