当我查看网络上的代码以及大量文献时,这一直是我的错误:为什么我们乘以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>
答案 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 = 0
和max = 256
。
如果完全遵循“配方”Int-B,则获取代码:
0 + floor(random() * (256 - 0))
如果删除所有零,则只剩下
floor(random() * 256)
并且您不需要&
与0xFF
,因为您从不获得256
(只要您的随机数生成器保证永不返回1
)。
答案 1 :(得分:0)
我认为你的问题被误导了。看起来你开始假设有一些"公平规则"强制执行正确的方式"翻译不幸的是,实际情况并非如此。如果您只想生成随机颜色,那么您可以使用适合您的任何逻辑。但是,如果您进行实际的图像处理,则没有规则说每个整数值必须映射到浮点值的相同间隔。相反,您真正想要的是两个包含区间[0;1]
和[0;255]
之间的映射。而且,当实际显示颜色时,您通常不知道在[0;1]
范围内将有多少真正的离散化步骤。 (在现代显示器上,每种颜色可能有256个不同的级别,但在其他输出设备上可能会有明显更少的选择,总数可能不是2的幂)。并且真正的映射规则是,如果对于两种颜色,红色分量值是R1
和R2
那么实际颜色的比例'红色组件亮度应尽可能接近R1:R2
。当你想要映射到255
时,这个规则自动意味着乘以[0;255]
,这就是每个人所做的事情。
请注意,您建议的最有可能是 引入错误 而不是修复错误。例如,比例规则实际上意味着您可以使用混合系数R1
和R2
计算两种颜色k1
和k2
的混合
Rmix = (k1*R1 + k2*R2)/(k1+k2)
现在让我们尝试用两种方式计算{100}红色与100%黑色(即0%红色)的混合物3:1
:
[0-255]
整数Rmix = (255*3+1*0)/(3+1) = 191.25 ≈ 191
[0;1]
浮动范围,然后将其转换为[0-255]
Rmix_float = (1.0*3 + 1*0.0)/(3+1) = 0.75
,以便Rmix_converted_256 = 256*0.75 = 192
。 这意味着你的"乘以256"逻辑实际上引入了不同结果的不一致性,具体取决于您用于图像处理的比例。显然,如果你使用"乘以255"像其他人一样做的逻辑,你得到一致的回答Rmix_converted_255 = 255*0.75 = 191.25 ≈ 191
。