2009-06-25 16 views
7

¿Cómo puedo convertir un búfer BYTE (de 0 a 255) en un búfer flotante (de 0.0 a 1.0)? Por supuesto, debería existir una relación entre los dos valores, por ejemplo: 0 en byte buffer será .0.f en float buffer, 128 en byte buffer será .5f en float buffer, 255 en byte buffer será 1.f in búfer flotanteConvertir el búfer BYTE (0-255) en el búfer flotante (0.0-1.0)

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 funciona muy lento. Un amigo mío me sugirió que usara una tabla de conversión, pero quería saber si alguien más puede darme otro enfoque.

+1

Para completar, 128 en el búfer de bytes será .5019607843f en el búfer flotante, no .5f. –

Respuesta

9

Si elige usar una tabla de búsqueda o no, su código está haciendo un montón de trabajo en cada iteración de bucle que realmente no necesita, lo suficientemente probable para eclipsar el costo de la conversión y multiplicar.

Declara tus punteros restrictivos, y los punteros solo lees de const. Multiplique por 1/255 en lugar de dividir por 255. No calcule los punteros en cada iteración del ciclo interno, solo calcule los valores iniciales e increméntelos. Desenrolle el lazo interno unas pocas veces. Use las operaciones vectoriales SIMD si su objetivo lo admite. No incremente y compare con el máximo, decremente y compare con cero en su lugar.

Algo así 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; 
} 

sería un buen comienzo.

+1

Algunas muy buenas sugerencias. Pero no superarán una tabla de búsqueda. ;-) –

+1

Depende de la arquitectura. Multiplicar y convertir puede ser más barato que cargar, especialmente si puede usar las capacidades SIMD de su arquitectura (MMX, SSE, Altivec o lo que sea) para hacerlo en todo el píxel en una sola instrucción. Pero esa decisión puede tomarse independientemente de todas las sugerencias anteriores. – moonshadow

+0

Esto hará más para facilitar el trabajo del compilador que para mejorar realmente la velocidad. Excepto alinear punteros y habilitar SIMD, puede dar un verdadero impulso – ima

2

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

1

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

También puede optimizar el lazo un poco quitando 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; 
} 
2

Es necesario averiguar cuál es el cuello de botella es:

  • si iterar sus datos tablas en la dirección "incorrecta", constantemente golpeas una falla de caché. Ninguna búsqueda lo ayudará a superar eso.
  • si su procesador tiene una escala más lenta que al mirar hacia arriba, puede aumentar el rendimiento al mirar hacia arriba, 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()); 
0

no calculan 1/255 cada vez. No sé si un compilador será lo suficientemente inteligente como para eliminar esto. Calcule una vez y vuelva a aplicarla cada vez. Mejor aún, defínalo como una constante.

+0

Los compiladores realizan plegamientos constantes, así que esto no es un problema. –

1

tabla de consulta es la manera más rápida para convertir :) Aquí van: código

Python para generar el byte_to_float.h archivo 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 el código C++ para obtener la conversión:

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

simple no?

8

Sé que esta es una vieja pregunta, pero dado que 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 como int a float conversión en mi ordenador (CPU Core 2 Duo).

Aquí hay una versión SSE3 del código anterior que hace 16 flotadores a la vez. Requiere que bytepixel y floatpixel estén alineados en 128 bits, y que el tamaño total sea un múltiplo de 4. Tenga en cuenta que las conversiones integradas de SSE3 flotante no ayudarán mucho aquí, ya que requerirán una multiplicación adicional de todos modos. Creo que esta es la forma más corta de ir a la instrucción, pero si su compilador no es lo suficientemente inteligente, puede desear desenrollar y programar 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: mejorar la precisión mediante el uso de (f + c/b) * b en lugar de f * b + c.

Editar: agregue la versión SSE3.

+0

Ahora, ¿no se puede hacer esto también utilizando los intrínsecos de SSE? Esto se ve como el ejemplo clásico de un código SIMD. (Lo mismo fue cierto para el código original ...) –

+0

¡Sí! SSE tiene funciones de barajado limitadas, pero pueden ser útiles aquí. –

Cuestiones relacionadas