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

ink*_*ibl 19 c language-agnostic colors

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

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

最后我用这个:

#define F2B(f) ((f) >= 1.0 ? 255 : (int)((f)*256.0))
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 26

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

b = floor(f >= 1.0 ? 255 : f * 256.0)
Run Code Online (Sandbox Code Playgroud)

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

f2 = max(0.0, min(1.0, f))
b = floor(f2 == 1.0 ? 255 : f2 * 256.0)
Run Code Online (Sandbox Code Playgroud)

替代安全解决方案

b = (f >= 1.0 ? 255 : (f <= 0.0 ? 0 : (int)floor(f * 256.0)))
Run Code Online (Sandbox Code Playgroud)

要么

b = max(0, min(255, (int)floor(f * 256.0)))
Run Code Online (Sandbox Code Playgroud)

  • "问题"来自闭合间隔而不是半闭间隔.没有一个间隔比其他间隔略大的话,没有办法解决这个问题.通过知道区间[0,1]中的浮点分布不均匀(它们在零附近更密集地填充)来控制自己,因此不能保证其他区间也是相同的大小. (9认同)
  • 唯一让我担心的是,现在 255 的范围突然比其他所有范围都略高;)。 (2认同)
  • 255应该涵盖从0.99609375到1.0的值。该答案建议将1.0包括在间隔中。确实,这是非常微妙的。对我来说,这是最好的答案。 (2认同)

Too*_*eve 9

我一直都这样做round(f * 255.0).

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

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

可能的好处是[我相信] 对于更广泛的输入值,RGB组件的相对值(稍微)更准确.
虽然我没有试图证明这一点,但这是我的直觉,因为对于每个组件我都会得到最接近的可用整数.(例如,我相信如果一个颜色有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
Run Code Online (Sandbox Code Playgroud)

朝着这个方向前进,毫无疑问是否使用255256:上述公式产生等间距结果的公式.观察它使用255.

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

图表使用3两位,类似于2558位.转换可以是从上到下,也可以是从下到上:

0 --|-- 1 --|-- 2 --|-- 3  
0 --|--1/3--|--2/3--|-- 0
   1/6     1/2     5/6
Run Code Online (Sandbox Code Playgroud)

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

如果你掌握了这些图表,你就会理解为什么我喜欢255基于公式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
对于每个范围,平均往返误差是范围的一半,so 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
Run Code Online (Sandbox Code Playgroud)

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

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

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


Eri*_*ler 7

为什么不尝试类似的东西

b=f*255.999
Run Code Online (Sandbox Code Playgroud)

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

  • inkredibl:你会很难找到差异真正重要的例子...... (5认同)