Mic*_*zic 1 algorithm graphics image-processing dithering
在常规 24 bpp 显示器上显示每通道超过 8 位的最忠实图形的最佳方法是什么?
我能想到的最佳解决方案是基于每帧都会改变的随机抖动。这结合了抖动与没有固定抖动模式的优点,并且由于给定的像素每秒更改多次值,因此您感知的值更接近于这些不同值的平均值,更接近于原始的“深色”值。任何给定的 24 bpp 值。
绿色渐变、未抖动、抖动(显示 10 帧),然后以相同的方式增强可见性:




抖动是通过将每个通道的伽马压缩深色值与随机值相加,然后四舍五入到最接近的 8 位值来实现的。使用均匀分布在 -0.5 和 0.5 之间的随机数似乎很自然(我所说的单位相当于 8 位伽马压缩值中的 1,例如 0 和 1 或 254 和 255 之间的差异) ,然而这会导致一种带状伪影,其中接近 8 位值的梯度值几乎没有噪声,而离任何 8 位值最远的值会显示更多的噪声。高斯噪声更合适,因为它提供更平滑的噪声水平。我选择了 1.0 的 sigma,但为了减少噪音,0.8 的 sigma 也可以。
您可以通过取两个随机数n1和来创建高斯 PRNG n2,将它们分别拟合在 [-1 , 1] 范围内,并且如果它们代表单位圆内的一个点(如果sum它们的平方和小于或等于 1 ,否则重新开始)返回sqrt(-2. * log(sum) / sum) * n1。
我选择通过将每通道 15 位线性 RGB 帧缓冲区转换为每通道 8 位 sRGB 帧缓冲区来实现这一点。线性到 sRGB 部分只是一个细节,我使用查找表将线性值转换为伽马压缩值(我选择使这些中间值使用 13 位,您可以将其视为 sRGB 值的 8.5 定点表示法)。
不用说,您不会为每个像素生成一个新的随机高斯数,您需要预先计算一堆它们并将它们放入循环缓冲区中。我选择制作 16384 个,是的,只有 16384 个,我通过在这个缓冲区中选择一个随机入口点来避免任何重复模式,一个随机长度(在 100 和 1123 之间,这是非常任意的),当我到达时长度的结尾我选择了一个新的随机起点和一个新的随机长度。通过这种方式,我可以从相对较小的数字缓冲区中获得非常随机的非重复模式。缓冲区中的数字以 2.5 定点格式存储,这样它们都在 -4.0 和 4.0 之间,涵盖了我想要的高斯随机数的范围。只要确保添加0。
以下是每个像素和每个通道的基本工作原理:
15 位线性值--通过 LUT--> 13 位(8.5 定点)伽马压缩值,然后添加 2.5 定点随机数,然后向右移动 5 位。
现在您得到一个介于 -4 和 260 之间的整数值,您可以使用 if()s 来限制它们,但使用 264 元素 LUT 会更快,该 LUT 为负数返回 0(您可以使用负数作为索引通过分配你的缓冲区然后做 buffer = &buffer[4], 为你节省了一个额外的我猜)并且对于大于 255 的数字返回 255。此外,我对三个颜色通道中的每一个都使用相同的随机数,这避免了色度噪声,尽管可以说是如果这三个使用独立数字,结果可能看起来不那么嘈杂。
对于单个像素的红色通道,我的代码如下所示:
sfb[i].r = bytecheck_l.lutb[lsrgb_l.lutint[fb[i].r] + dither_l.lutint[id] >> 5];
sfb 是 sRGB 24 bpp 缓冲区,fb 是 45 bpp 线性 RGB 缓冲区,lsrgb_l.lutint[] 是伽马压缩 LUT 的线性,dither_l.lutint[] LUT包含 2.5 定点格式的随机高斯数和 bytecheck_l.lutb[] 返回值裁剪为 [0 , 255]。
我仅使用 2.4 GHz Core 2 Quad Q6600 和双通道 800 MHz DDR2 内存中的一个内核,在 1400x820 SDL 窗口中获得超过 50 FPS 的测试梯度,按照当前标准,这是一台有点平庸的机器,因此该解决方案似乎绝对适合现代电脑。
如果我的任何解释需要澄清,请告诉我。