【发布时间】:2013-03-25 11:55:18
【问题描述】:
在常规 24 bpp 显示器上显示每通道超过 8 位的最忠实图形的最佳方式是什么?
【问题讨论】:
标签: algorithm image-processing graphics dithering
在常规 24 bpp 显示器上显示每通道超过 8 位的最忠实图形的最佳方式是什么?
【问题讨论】:
标签: algorithm image-processing graphics dithering
我能想到的最佳解决方案是基于改变每一帧的随机抖动。这结合了抖动与不具有固定抖动模式的优点,并且由于给定像素每秒多次更改值,因此您认为更接近这些不同值的平均值,这更接近原始“深色”值而不是任何给定的 24 bpp 值。
绿色渐变,无抖动,抖动(显示 10 帧),然后以相同的方式增强可见性:
抖动是通过将每个通道的伽马压缩深色值与随机值相加,然后四舍五入到最接近的 8 位值来实现的。使用在 -0.5 和 0.5 之间均匀分布的随机数似乎很自然(我所说的单位相当于 8 位伽马压缩值中的 1,例如 0 和 1 或 254 和 255 之间的差异) ,但是这会导致一种带状伪影,其中接近 8 位值的梯度值几乎没有噪声,而离任何 8 位值最远的值会显示更多的噪声。高斯噪声更合适,因为它提供了更平滑的噪声水平。我选择了 1.0 的 sigma,但为了减少噪音,0.8 的 sigma 可能会做。
您可以通过取两个随机数 n1 和 n2 来创建高斯 PRNG,将它们分别拟合在 [-1 , 1] 范围内,如果它们代表单位圆内的一个点(如果总和sum他们的方格小于等于1,否则重新开始)返回sqrt(-2. * log(sum) / sum) * n1。
我选择通过将每通道 15 位的线性 RGB 帧缓冲区转换为每通道 8 位的 sRGB 帧缓冲区来实现这一点。 sRGB 的线性部分只是一个细节,我使用查找表将线性值转换为 gamma 压缩值(我选择使这些中间值使用 13 位,您可以将其视为 sRGB 值的 8.5 定点符号)。
不用说,您不会为每个像素生成一个新的随机高斯数,您需要预先计算一堆并将它们放入循环缓冲区中。我选择了 16384 个,是的,只有 16384 个,我通过在这个缓冲区中选择一个随机入口点来避免任何重复模式,一个随机长度(在 100 到 1123 之间,这是非常任意的),当我到达长度的结束我选择了一个新的随机起点和一个新的随机长度。通过这种方式,我可以从相对较小的数字缓冲区中获得相当随机的非重复模式。缓冲区中的数字以 2.5 定点格式存储,这样它们都在 -4.0 和 4.0 之间,涵盖了我想要的高斯随机数范围。只需确保将 0.5 添加到您的随机数上,因为这将在以后四舍五入到最接近的整数。
这基本上是每个像素和每个通道的工作原理:
15 位线性值--通过 LUT--> 13 位(8.5 定点)伽马压缩值,然后添加 2.5 定点随机数,然后向右 SHIFT 5 位。
现在你得到一个介于 -4 和 260 之间的整数值,你可以使用 if()s 来限制这些值,但是使用 264 元素 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[] 包含 2.5 定点格式的随机高斯数和 bytecheck_l 的 LUT .lutb[] 返回剪辑为 [0 , 255] 的值。
我在 1400x820 SDL 窗口中获得超过 50 FPS 的测试梯度,仅使用 2.4 GHz Core 2 Quad Q6600 的一个内核和双通道 800 MHz DDR2 内存,按当前标准来看,这台机器有点平庸,所以这个解决方案看起来肯定适用于现代计算机。
如果我的任何解释需要澄清,请告诉我。
【讨论】: