Пушыстый (_winnie) wrote,
Пушыстый
_winnie

Category:

упаковка float 01 <-> byte

Часто значения float из промежутка [0, 1] хранят в виде байта или в двух байтах, для экономии места.

Когда незадумчивые программисты пишут код конвертации float2byte и byte2float, возникают следующие ошибки:

1) float2byte( byte2float (X) ) != X
2) float2byte( 1.0 ) отображается в 256, то есть в байт 0.
3) float2byte( byte2float (X) + небольшой шум ) != X
4) часто важно что бы byte2float(255) == 1.0 и byte2float(0) == 0.0
5) множества float соответствующие значению байта - имеют разный размер. Напр. 0 и 255 отображаются в промежутки длины 1/510, а 1,2,..254 - в промежутки 1/255

1,2 - тупо багло. 3,4,5 - важны, но иногда можно пожертвовать, например часто забивают на 5 без особых проблем, или даже с пользой если есть выход за пределы [0..1] в промежуточных вычислениях.

Формулы которые пишут незадумчиво с налёту - это обычно {b=int(f*255.0), f=b/255.0}, {b=min(int(f*256.0),255), f=b/256.0} и тп.

Получают небольшие изменения яркости при каждом save/load, странные шумы, превращение 255 в 254, белый цвет умноженый на белый - даёт не белый, и тп.

Что бы избежать этих ошибок, надо представлять себе такую картинку: делим отрезок [0..1] на 256 равных отрезков (полуоткрытых интервалов). Байты должны превращаться в серединки или другие внутренности этих отрезков, а эти отрезки - в байты, взаимно однозначно. Вариант: промежутки для 1,2,..254 одинаковые, а для 0 и 255 - в два раза меньше.

Примеры устойчивых упаковок:

byte2float:
   f = (b/255.0f - чуть меньше устойчиво к шуму, max-шума=1/(255*256). Зато 0 и 255 превращаются в 0.0f и 1.0f.
   или f = (b + 0.5f)/256.0f - устойчиво к шуму, max-шума=1/(2*256), но 255 отображается в 511.0/512.0 а не в 1.0. Белый цвет умноженый на белый много раз станет серым.

float2byte:
   b = min( int(f*256), 255 )
   или b = int(f*255.9999f)

И ещё вариант, классический, спасибо zeux за его комментарий (рекомендую, если сомневаетесь что выбрать):

byte2float:
   f = b/255.0f
float2byte:
   b = int(f*255 + 0.5)
Хорош всем, только интервалы для 0 и 255 в два раза меньше чем для 1,2,..,254.



Увы, многие библиотеки об этом не задумываются. Если у вас есть многократный save load с преобразованием float/byte, сделайте code review в этом месте. Это могут быть упакованые анимации, heighmap ландшафтов, очевидно текстуры, веса костей для скелетной анимации, всякие веса смешиваний и долей. В базах данных тоже любят оптимизировать такие float.


#include <algorithm> #include <assert.h> typedef unsigned char byte; float clamp(float f) { return std::max(std::min(f, 1.0f), 0.f); } byte f2b_1(float f) { f = clamp(f); return int(f*255.9999f); } byte f2b_2(float f) { f = clamp(f); return std::min(int(f*256.f), 255); } float b2f_1(byte b) { return (b+0.5f) / 256.0f; } float b2f_2(byte b) { return b / 255.0f; } //recommended int main() { for (int i = 0; i < 256; ++i) { byte b = i; assert( f2b_1(b2f_1(b)) == b ); assert( f2b_1(b2f_2(b)) == b ); assert( f2b_2(b2f_1(b)) == b ); assert( f2b_2(b2f_2(b)) == b ); float eps = 0.00001; float big_eps = 0.001; assert( f2b_1(b2f_1(b) + big_eps) == b ); assert( f2b_1(b2f_2(b) + eps) == b ); assert( f2b_2(b2f_1(b) + big_eps) == b ); assert( f2b_2(b2f_2(b) + eps) == b ); assert( f2b_1(b2f_1(b) - big_eps) == b ); assert( f2b_1(b2f_2(b) - eps) == b ); assert( f2b_2(b2f_1(b) - big_eps) == b ); assert( f2b_2(b2f_2(b) - eps) == b ); } assert(f2b_1(0.00001f) == 0); assert(f2b_1(0) == 0); assert(f2b_1(0.99999f) == 255); assert(f2b_1(1) == 255); assert(f2b_2(0.00001f) == 0); assert(f2b_2(0) == 0); assert(f2b_2(0.99999f) == 255); assert(f2b_2(1) == 255); assert(b2f_2(0) == 0); assert(b2f_2(255) == 1.0); }
_Winnie C++ Colorizer

Tags: geom-prog, math, paint mad skilz, soft-dev, tips
Subscribe

  • CR LF

    В некоторых файлах в некоторых контекстах надо байты '\r\n' интерпретировать так же, как как байт '\n' (например, чтение значения из рукописного…

  • Что такое оттенок цвета.

    Матовый объект, который отражает падающий свет во все стороны, не меняя его спектральный состав - белый, т.е. бесцветный. Если свет отражается…

  • mspaint circles

    По ходу работы скриптов рисовал в задумчивости кружочки по-быстрому (под рукой оказался mspaint), внезапно вылезло вот такое:

  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 34 comments

  • CR LF

    В некоторых файлах в некоторых контекстах надо байты '\r\n' интерпретировать так же, как как байт '\n' (например, чтение значения из рукописного…

  • Что такое оттенок цвета.

    Матовый объект, который отражает падающий свет во все стороны, не меняя его спектральный состав - белый, т.е. бесцветный. Если свет отражается…

  • mspaint circles

    По ходу работы скриптов рисовал в задумчивости кружочки по-быстрому (под рукой оказался mspaint), внезапно вылезло вот такое: