将颜色值从float 0..1转换为byte 0..255

将颜色值从float转换为byte的正确方法是什么? 起初我以为b=f*255.0应该这样做,但现在我想,在这种情况下,只有精确的1.0将被转换为255 ,但0.9999已经是254 ,这可能不是我想要的……

似乎b=f*256.0会更好,除非它会在精确1.0的情况下产生256的不需要的情况。

最后我用这个:

 #define F2B(f) ((f) >= 1.0 ? 255 : (int)((f)*256.0)) 

1.0是唯一可能出错的情况,所以单独处理该案例:

 b = floor(f >= 1.0 ? 255 : f * 256.0) 

此外,值得强制的是f确实为0 <= f <= 1以避免由于舍入误差而导致的错误行为(例如,f = 1.0000001)。

 f2 = max(0.0, min(1.0, f)) b = floor(f2 == 1.0 ? 255 : f2 * 256.0) 

替代安全解决方案

 b = (f >= 1.0 ? 255 : (f <= 0.0 ? 0 : (int)floor(f * 256.0))) 

要么

 b = max(0, min(255, (int)floor(f * 256.0))) 

为什么不尝试类似的东西

 b=f*255.999 

摆脱特殊情况f==1但0.999仍然是255

我总是做round(f * 255.0)

不需要进行测试(1的特殊情况)和/或其他答案中的钳位。 这是否适合您的目的取决于您的目标是尽可能地匹配输入值[我的公式],还是将每个组件划分为256个相等的间隔[其他公式]。

我公式的可能缺点是0和255区间只有其他区间宽度的一半。 经过多年的使用,我还没有看到任何视觉证据表明这是坏事。 相反,我发现在输入非常接近它之前最好不要达到任何极端 – 但这是一个品味问题。

可能的好处是[我相信] RGB组件的相对值(稍微)更精确,适用于更宽范围的输入值。
虽然我没有试图certificate这一点,但这是我的直觉,因为对于每个组件我都会得到最接近的可用整数。 (例如,我相信如果一个颜色有G~ = 2 x R,这个公式通常会保持接近这个比例;虽然差异非常小,但256公式有更好的其他颜色。所以它可能是洗。)

在实践中,基于256255的方法似乎提供了良好的结果。


另一种评估 255 vs 256是检查另一个方向 –
从0..255字节转换为0.0..1.0浮点数。

将0..255个整数值转换为0.0..1.0范围内等间距值的公式为:

 f = b / 255.0 

朝着这个方向前进,毫无疑问是使用255还是256 :上面的公式产生等间距结果的公式。 注意它使用255

要理解两个方向上255公式之间的关系,请考虑此图,如果您只有2位,则值为整数值0..3:

图中使用3表示两位,类似于255表示8位。 转换可以是从上到下,也可以是从下到上:

 0 --|-- 1 --|-- 2 --|-- 3 0 --|--1/3--|--2/3--|-- 0 1/6 1/2 5/6 

| 是4个范围之间的界限。 观察到在内部,浮点值和整数值位于其范围的中点。 观察两个表示中所有值之间的间距是恒定的。

如果您掌握这些图表,您就会明白为什么我喜欢基于256的公式而不是基于256的公式。


声明 :如果字节到浮点数时使用/ 255.0 ,但从浮点数转到字节时不使用round(f * 255.0)则会增加“平均往返”错误 。 细节如下。

这最容易通过从float开始,到byte,然后再到float来测量。 要进行简单分析,请使用2位“0..3”图表。

从大量浮点值开始,均匀间隔0.0到1.0。 往返将所有这些值分组为4值。
该图有6个半间隔长度范围:
0..1 / 6,1 / 6..1 / 3,..,5 / 6..1
对于每个范围,平均往返误差是范围的一半,因此1/12 (最小误差为零,最大误差为1/6,均匀分布)。
所有范围都给出了同样的错误; 往返时, 1/12是总体平均误差。

如果您改为使用* 256* 255.999公式中的任何一个,则大多数往返结果都是相同的,但有一些会移动到相邻范围。
对另一个范围的任何更改都会增加错误 ; 例如,如果先前单个浮点输入的误差略小于 1/6,则返回相邻范围的中心会导致误差略大于1/6。 例如,最佳公式中的0.18 =>字节1 =>浮动1/3〜= 0.333,对于错误| 0.33-0.18| = 0.147 ; 使用256公式=> byte 0 => float 0,对于误差0.18 ,这是从最佳误差0.147增加。

使用* 4/ 3图表。 转换是从一行到下一行。
注意第一行的间距不均匀:0..3 / 8,3 / 8..5 / 8,5 / 8..1。 那些距离是3 / 8,2 / 8,3 / 8。 请注意,最后一行的间隔边界与第一行不同。

  0------|--3/8--|--5/8--|------0 1/4 1/2 3/4 => 0------|-- 1 --|-- 2 --|------3 => 0----|---1/3---|---2/3---|----0 1/6 1/2 5/6 

避免这种增加的错误的唯一方法是在从byte到float时使用一些不同的公式。 如果您坚信256公式中的一个,那么我将留给您确定最佳的反公式。
(每个字节值,它应该返回浮点值的中点,该值变为该字节值。除了0到0,以及3到1.或者可能是0到1 / 8,3到7/8!在上图中,它应该从中线回到顶线。)

但是现在你将拥有难以防御的情况,你已经采用了等间隔的字节值,并将它们转换为非等间距的浮点值。

如果您使用除了精确255之外的任何值,那么这些是您的选项,对于整数0..255:平均往返误差的增加,或浮点域中的非均匀间隔值。

当它比较float时,接受的解决方案失败,因为它是整数。

这段代码工作正常:

 float f; uint8_t i; //byte to float f =CLAMP(((float)((i &0x0000ff))) /255.0, 0.0, 1.0); //float to byte i =((uint8_t)(255.0f *CLAMP(f, 0.0, 1.0))); 

如果你没有CLAMP:

 #define CLAMP(value, min, max) (((value) >(max)) ? (max) : (((value) <(min)) ? (min) : (value))) 

或者对于全RGB:

 integer_color =((uint8_t)(255.0f *CLAMP(float_color.r, 0.0, 1.0)) <<16) | ((uint8_t)(255.0f *CLAMP(float_color.g, 0.0, 1.0)) <<8) | ((uint8_t)(255.0f *CLAMP(float_color.b, 0.0, 1.0))) & 0xffffff; float_color.r =CLAMP(((float)((integer_color &0xff0000) >>16)) /255.0, 0.0, 1.0); float_color.g =CLAMP(((float)((integer_color &0x00ff00) >>8)) /255.0, 0.0, 1.0); float_color.b =CLAMP(((float)((integer_color &0x0000ff))) /255.0, 0.0, 1.0); 

我认为正确的是楼层(f * 256),而不是圆形。 这会将间隔0..1映射到相等长度的256个区域。

[编辑]并检查256作为特例。

 public static void floatToByte(float f) { return (byte)(f * 255 % 256) } 

值<1被准确转换。

转换后,介于255和256之间的值在转换为字节时会浮动到255。

值> 1使用%运算符循环回0。