它是一个通过乘以255将0.0-1.0浮点数缩放到字节的错误?

时间:2018-01-19 23:33:59

标签: math colors type-conversion

当我查看网络上的代码以及大量文献时,这一直是我的错误:为什么我们乘以255而不是256?

有时你会看到类似的东西:

float input = some_function(); // returns 0.0 to 1.0
byte output = input * 255.0;

(我假设在类型转换过程中存在隐式floor

我认为这显然是错误的,我不正确吗?

考虑一下:

  • input的范围是0的输出? (0 - > 1/255),对吧?
  • input的范围是1的输出? (1/255 - > 2/255],太棒了!
  • input的范围是255的输出? 1.0。任何显着较小的input值都会返回较低的output

这意味着input 事件映射到输出范围。

确定。所以你可能会想:确定使用更好的舍入函数:

byte output = round(input * 255.0);

其中round()是通常的数学舍入到零小数位。但这仍然是错误的。问同样的问题:

  • input的范围是0的输出? (0 - > 0.5 / 255]
  • input的范围是1的输出? (0.5 / 255 - > 1.5 / 255],是0的两倍!
  • input的范围是255的输出? (254.5 / 255 - > 1.0),也是1
  • 的一半

所以在这种情况下输入范围也不是均匀映射的!

IMHO。执行此映射的正确方法是:

byte output = min(255, input * 256.0);

再次:

  • input的范围是0的输出? (0 - > 1/256)
  • input的范围是1的输出? (1/256 - > 2/256)
  • input的范围是255的输出? (255/256 - > 1.0)

所有这些范围都是相同的大小,构成输入的1/256。

我想我的问题是:我正确地考虑这个错误,如果是这样,为什么这在代码中如此普遍?

编辑:看起来我需要澄清一下。我不是在谈论这里的随机数或概率。我根本不是谈论颜色或硬件。我正在谈论将范围[0,1]中的浮点均匀地转换为字节[0,255],因此输入中与输出中的每个值对应的每个范围都是相同的大小。 / p>

2 个答案:

答案 0 :(得分:2)

你是对的。假设valueBetween0and1可以取值0.0和1.0,那么“正确”的方法就像

byteValue = (byte)(min(255, valueBetween0and1 * 256))

话虽如此,人们还可以争辩说,软件所需的质量可能会有所不同:在一些丢弃的情节中,你是否得到16777216或16581375颜色真的很重要吗? 这是“琐碎”任务之一,很容易被+ 1 / -1错误。是否值得花5分钟试图获得第255个像素强度,或者你可以将你的宝贵注意力应用到其他地方?这取决于具体情况:(byte)(valueBetween0and1 * 255)是一个实用的解决方案,它简单,便宜,足够接近事实,并且立即显然是“无害的”,因为它肯定不会产生256作为输出。如果您正在使用像Photoshop这样的图像处理工具,或者如果您正在为计算机游戏开发一些渲染管道,那么这不是一个好的解决方案。但它几乎在所有其他情况下都是完全可以接受的。因此,无论是“错误”还是仅仅是一个小改进建议取决于具体情况。

以下是您的问题的变体,其中涉及随机数生成器: Generate random numbers in specified range - various cases (int, float, inclusive, exclusive) 请注意,例如Java中的Math.random()或C#中的Random.NextDouble返回的值大于或等于0,但严格小于1.0

您希望案例“Integer-B:[min,max)”(包含 - 排除)与min = 0max = 256

如果完全遵循“配方”Int-B,则获取代码:

0 + floor(random() * (256 - 0))

如果删除所有零,则只剩下

floor(random() * 256)

并且您不需要&0xFF,因为您从不获得256(只要您的随机数生成器保证永不返回1)。

答案 1 :(得分:0)

我认为你的问题被误导了。看起来你开始假设有一些"公平规则"强制执行正确的方式"翻译不幸的是,实际情况并非如此。如果您只想生成随机颜色,那么您可以使用适合您的任何逻辑。但是,如果您进行实际的图像处理,则没有规则说每个整数值必须映射到浮点值的相同间隔。相反,您真正想要的是两个包含区间[0;1][0;255]之间的映射。而且,当实际显示颜色时,您通常不知道在[0;1]范围内将有多少真正的离散化步骤。 (在现代显示器上,每种颜色可能有256个不同的级别,但在其他输出设备上可能会有明显更少的选择,总数可能不是2的幂)。并且真正的映射规则是,如果对于两种颜色,红色分量值是R1R2那么实际颜色的比例'红色组件亮度应尽可能接近R1:R2。当你想要映射到255时,这个规则自动意味着乘以[0;255],这就是每个人所做的事情。

请注意,您建议的最有可能是 引入错误 而不是修复错误。例如,比例规则实际上意味着您可以使用混合系数R1R2计算两种颜色k1k2的混合

Rmix = (k1*R1 + k2*R2)/(k1+k2)

现在让我们尝试用两种方式计算{100}红色与100%黑色(即0%红色)的混合物3:1

  1. 使用[0-255]整数Rmix = (255*3+1*0)/(3+1) = 191.25 ≈ 191
  2. 使用[0;1]浮动范围,然后将其转换为[0-255] Rmix_float = (1.0*3 + 1*0.0)/(3+1) = 0.75,以便Rmix_converted_256 = 256*0.75 = 192
  3. 这意味着你的"乘以256"逻辑实际上引入了不同结果的不一致性,具体取决于您用于图像处理的比例。显然,如果你使用"乘以255"像其他人一样做的逻辑,你得到一致的回答Rmix_converted_255 = 255*0.75 = 191.25 ≈ 191