Question

Comment puis-je convertir un tampon BYTE (de 0 à 255) en un tampon flottant (de 0,0 à 1,0)? Bien entendu, il devrait exister une relation entre les deux valeurs, par exemple: 0 dans le tampon d'octets sera .0.f dans le tampon flottant, le tampon dans 128 octets sera de 0,5f dans le tampon flottant, le tampon dans 255 dans le tampon d'octets sera de 1 dans float buffer.

En fait, voici le code que j'ai:

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

Ceci est très lent. Un de mes amis m'a suggéré d'utiliser une table de conversion, mais je voulais savoir si quelqu'un d'autre pouvait me donner une autre approche.

Était-ce utile?

La solution

Que vous choisissiez ou non d'utiliser une table de recherche, votre code effectue beaucoup de travail à chaque itération de boucle dont il n'a pas réellement besoin - assez pour occulter le coût de la conversion et se multiplier.

Déclarez vos pointeurs restrictifs et les pointeurs que vous ne lisez que de const. Multipliez par 1 / 255ème au lieu de diviser par 255. Ne calculez pas les pointeurs à chaque itération de la boucle interne, calculez simplement les valeurs initiales et incrémentez-les. Déroulez la boucle intérieure à quelques reprises. Utilisez les opérations SIMD vectorielles si votre cible le prend en charge. Ne pas incrémenter et comparer avec maximum, décrémenter et comparer avec zéro à la place.

Quelque chose comme

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

serait un début.

Autres conseils

Je sais que c’est une vieille question, mais puisque personne n’a donné de solution en utilisant la représentation float IEEE, en voici une.

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

Cela représente plus de deux fois plus rapidement qu'une conversion int en float sur mon ordinateur (processeur Core 2 Duo).

Voici une version SSE3 du code ci-dessus qui fait 16 flottants à la fois. Il faut que bytepixel et floatpixel soient alignés sur 128 bits et que la taille totale soit un multiple de 4. Notez que les conversions int intégrées à float SSE3 ne aider beaucoup ici, car ils nécessiteront une multiplication supplémentaire de toute façon. Je pense que c’est le moyen le plus rapide de suivre des instructions, mais si votre compilateur n’est pas assez intelligent, vous voudrez peut-être dérouler et programmer les choses à la main.

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

Modifier : améliorez la précision en utilisant (f + c / b) * b au lieu de f * b + c .

Modifier : ajoutez la version SSE3.

Utilisez une table de consultation statique pour cela. Lorsque je travaillais dans une société d’infographie, nous avions une table de correspondance codée en dur que nous avons liée au projet.

Vous devez savoir quel est le goulot d'étranglement:

  • si vous parcourez vos tables de données dans la "mauvaise" direction, vous rencontrez constamment un manque de mémoire cache. Aucune recherche ne vous aidera jamais à contourner ce problème.
  • Si la taille de votre processeur est plus lente que la recherche, vous pouvez améliorer les performances en recherchant, à condition que la table de recherche corresponde à son cache.

Autre astuce:

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

Oui, une table de recherche est nettement plus rapide que de faire beaucoup de divisions dans une boucle. Générez simplement une table de 256 valeurs flottantes précalculées et utilisez la valeur d'octet pour indexer cette table.

Vous pouvez également optimiser un peu la boucle en supprimant le calcul de l'index et en faisant quelque chose comme

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

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

La table de conversion est le moyen le plus rapide de convertir :) Voilà:

Code Python pour générer le fichier byte_to_float.h à inclure:

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

Et du code C ++ pour obtenir la conversion:

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

Simple n'est-ce pas?

Ne calculez pas 1/255 à chaque fois. Je ne sais pas si un compilateur sera assez intelligent pour supprimer cela. Calculez-le une fois et réappliquez-le à chaque fois. Mieux encore, définissez-le comme une constante.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top