Domanda

Come posso convertire un buffer BYTE (da 0 a 255) in un buffer float (da 0,0 a 1,0)? Ovviamente ci dovrebbe essere una relazione tra i due valori, ad es .: 0 nel buffer dei byte sarà .0.f nel buffer float, 128 nel buffer byte sarà .5f nel buffer float, 255 nel buffer byte sarà 1.f in float buffer.

In realtà questo è il codice che ho:

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

Funziona molto lentamente. Un mio amico mi ha suggerito di utilizzare una tabella di conversione, ma volevo sapere se qualcun altro può darmi un altro approccio.

È stato utile?

Soluzione

Indipendentemente dal fatto che tu scelga di utilizzare una tabella di ricerca o meno, il tuo codice sta facendo molto lavoro in ogni iterazione di loop che in realtà non è necessario, probabilmente abbastanza per oscurare il costo della conversione e moltiplicare.

Dichiara i tuoi puntatori limitati e quelli che leggi solo da const. Moltiplicare per 1 / 255th invece di dividere per 255. Non calcolare i puntatori in ogni iterazione del ciclo interno, basta calcolare i valori iniziali e incrementarli. Srotolare il circuito interno alcune volte. Utilizzare le operazioni SIMD vettoriali se il target lo supporta. Non incrementare e confrontare con il massimo, decrementare e confrontare con zero invece.

Qualcosa di simile

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

sarebbe un inizio.

Altri suggerimenti

So che questa è una vecchia domanda, ma poiché nessuno ha fornito una soluzione utilizzando la rappresentazione float IEEE, eccone 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;
}

Questo è più di due volte più veloce rispetto a una conversione da int a float sul mio computer (CPU Core 2 Duo).

Ecco una versione SSE3 del codice sopra che fa 16 float alla volta. Richiede bytepixel e floatpixel per essere allineati a 128 bit e la dimensione totale deve essere un multiplo di 4. Notare che le conversioni int incorporate in SSE3 per il float delle conversioni non lo faranno aiuta molto qui, poiché richiederanno comunque una moltiplicazione aggiuntiva. Credo che questo sia il modo più breve per seguire le istruzioni, ma se il tuo compilatore non è abbastanza intelligente potresti voler srotolare e pianificare le cose 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;
}

Modifica : migliora la precisione utilizzando (f + c / b) * b anziché f * b + c .

Modifica : aggiungi la versione SSE3.

Usa una tabella di ricerca statica per questo. Quando ho lavorato in una società di computer grafica abbiamo finito per avere una tabella di ricerca codificata per questo che abbiamo collegato al progetto.

Devi scoprire qual è il collo di bottiglia:

  • se si ripetono le tabelle dei dati nella direzione "errata", si verifica costantemente un errore nella cache. Nessuna ricerca potrà mai aiutare a evitarlo.
  • se il tuo processore è più lento nel ridimensionamento che nel cercare, puoi migliorare le prestazioni guardando in alto, a condizione che la tabella di ricerca si adatti alla sua cache.

Un altro suggerimento:

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

Sì, una tabella di ricerca è decisamente più veloce rispetto a fare molte divisioni in un ciclo. Basta generare una tabella di 256 valori float precompilati e utilizzare il valore byte per indicizzare quella tabella.

Puoi anche ottimizzare un po 'il ciclo rimuovendo il calcolo dell'indice e facendo qualcosa come

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

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

La tabella di ricerca è il modo più veloce per convertire :) Ecco qui:

Codice Python per generare il file byte_to_float.h da includere:

#!/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 codice C ++ per ottenere la conversione:

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

Semplice non è vero?

Non calcolare 1/255 ogni volta. Non so se un compilatore sarà abbastanza intelligente da rimuoverlo. Calcolalo una volta e riapplicalo ogni volta. Ancora meglio, definiscilo come costante.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top