RGB 值归一化问题抛出假设编写图像处理程序读取图像转换为浮点数格式处理后以 8 位颜色保存到磁盘那么整数到浮点数的转换该如何进行呢有标准的除以 255 方法和替代的除以 256 方法用 Python 和 NumPy 实现如下标准的除以 255 方法 | 替代的除以 256 方法---|---pythonpixels img / 255.0result process(pixels)output np.trunc(result * 255 0.5) |pythonpixels (img 0.5) / 256.0result process(pixels)output np.trunc(result * 256)且在最终类型转换之前两种情况下的输出值都会被裁剪python# 裁剪并转换为 8 位output_8bit output.clip(0, 255).astype(np.uint8)标准方法将整数 0 映射到 0.0将 255 映射到 1.0且 GPU 也是这么做的。替代方法增加 0.5 偏移量后除以 256整数 0 被映射到 0.001953125这使得图像处理代码若不知上述常量就无法检测黑色像素逻辑会与 8 位输入绑定而标准方法始终可假设黑色是 0.0。但有些程序员仍倾向于替代方法他们看中了它的什么呢反对除以 255 的理由极端区间较小当在数轴上绘制标准方法时它看起来很奇怪。以将范围在 [0..7] 的 3 位整数映射到 [0, 1] 为例标准公式的极端区间超出了 [0, 1] 的范围虽两种方法都会裁剪输出但它清楚显示了标准范围的“拉伸”这个拉伸后的范围比图像处理中假定的操作范围 [0, 1] 更宽。这意味着将 [0, 1] 范围内的浮点数值转换回整数时极端区间宽度只有其他区间的一半从算法中输出极端值“更难”。例如生成均匀分布的 [0, 1] 噪声并用标准公式四舍五入值 0 和 255 出现频率只有其他整数的一半。通过生成一百万个均匀随机数绘制直方图可验证这一说法突出显示的局部直方图代码pythonimport numpy as npimport matplotlib.pyplot as pltresult np.random.uniform(0, 1, 1000000)final_values np.trunc(result * 255 0.5).clip(0, 255).astype(np.uint8)plt.hist(final_values, bins256, range(0, 255))plt.show()不过很难想出这种远离极端值的偏差会带来问题的例子。标准方法浮点数分布范围更宽但原始图像仍可无损往返转换。而且略大于 0.0 或 1.0 的结果值仍会四舍五入到正确区间使输出分布均匀。例如处理过程从浮点颜色中减去 0.005标准方法中黑色值低于零替代方法中值仍为正但最终两种方法都会输出整数 0plaintext标准方法trunc(255 * (-0.005) 0.5) 0替代方法trunc(256 * (0.5 / 256 - 0.005)) 0所以标准方法中零区间只有“一半大小”并不重要。不精确性标准方法的浮点数值并不精确例如 128 / 255.0 ≈ 0.501961而 128 / 256.0 0.5。由于舍入误差浮点数值之间的距离有微小变化。但这并非真正问题32 位浮点数有 23 位小数部分讨论的是最低有效位的舍入误差抖动幅度小于 2⁻²³。在复杂图像处理任务中0.00001% 的相对误差无关紧要这种不精确性是美学问题而非技术问题。不在整数之间的值替代方法总是将每个浮点数值精确置于两个整数中间这个中间位置可看作折衷因不知原始量化值究竟是多少两个连续整数之间的平均值是不错的猜测。虽很难想出具体应用场景但安德鲁·凯斯勒在 2015 年博客文章《颜色深度转换》中认为抖动处理会更方便理由是添加噪声时无需担心边界情况相比之下标准公式极端情况较棘手需仔细处理才能保持噪声分布的一致性。两种类型的量化器可将这两种方法视为两种不同的均匀标量量化器。从维基百科了解到大多数用于有符号输入数据的均匀量化器分为中升型和中平型。中平型量化器有零值的重建级别中升型量化器有零值的分类阈值。维基百科引用了 1977 年的一篇论文标题和摘要布局独特。当中升型和中平型量化器绘制在图表上时它们在穿过零点的位置不同中平型将零映射到零中升型将零映射到两个整数中间。维基百科选择的符号用 x 表示输入的实数用 k 表示其编码后的整数值用 yₖ 表示重建后的实数。相应的量化器公式如下类型 | 分类编码 | 重建解码---|---|---中升型楼梯量化器 | k trunc(xL) | yₖ (k 0.5) / L中平型楼梯量化器 | k trunc(xL 0.5) | yₖ k / LL 表示不同输出级别的数量例如 256。将这些定义应用到两种竞争方法上标准公式可称为 L 255 的“中升型”替代方法为 L 256 的“中平型”。再次展示代码并加上新标签中升型量化器L 255 | 中平型量化器L 256---|---pythonpixels img / 255.0result process(pixels)output np.trunc(result * 255 0.5) |pythonpixels (img 0.5) / 256.0result process(pixels)output np.trunc(result * 256)从这个角度看标准方法是用于无符号输入的中升型量化器选择 L 255 的整数编码对 8 位输入并非最优这么做是为了编程方便让极端值映射到 0.0 和 1.0。这引出了对标准公式的最后一个批评。更高的量化误差但并非如此若设计系统接收均匀分布的实数 x ∈ [0, 1]编码为 8 位整数 k 再重建为另一个实数 yₖ标准公式会浪费带宽。标准方法中可表示值的范围是 [-0.5 / 255, 255.5 / 255]区间间隔比 [0, 1] 输入严格所需的更大导致更高的重建误差。不过误差增加很小根据 StackOverflow 用户彼得·穆德里耶夫斯基的计算对于除数 255 和 256平均绝对误差分别为 1 / 1020 和 1 / 1024所以除以 256 在理论上更精确。但实际是加载 8 位 RGB 图像处理后再保存无法控制保存时的量化丢失的信息无法恢复。若图像颜色乘以 255 并四舍五入加载时除以 256 不能恢复精度。只有同时控制保存和加载时降低重建误差的诉求才有意义。实际上用替代公式加载他人图像会引入更多误差因为这些图像可能是通过标准公式量化的用错误比例因子解码理论上不正确。在实践中颜色不是绝对测量值只是在稍小范围内处理并带有小偏移量。最后永远不要混合使用两种量化器的编码和解码步骤这很容易犯错。结论如果你处理的是陌生人提供的图像应该将 RGB 值除以 255 进行归一化不精确的浮点数值和更高重建误差的抽象感觉都不是选择替代方法的好理由。但如果你同时控制图像的保存和加载不需要将零映射到零且对将处理代码与 8 位动态范围绑定感到满意那么可以考虑除以 256 以获得更高的精度。只是当同事仍用标准公式加载你的图像时可别抱怨。其他观点乔纳森·布洛在 2002 年的文章中讨论了中升型和中平型量化器但未提及名称本文绘制图表的灵感来源于此。安德鲁·凯斯勒在 2015 年的博客文章中提倡使用替代公式但他将其与未进行四舍五入的标准公式比较使大部分分析无效。作者正在写一本关于颜色缩减算法的书感兴趣可注册。
RGB 值归一化:除以 255 还是 256?各有优劣引探讨!
RGB 值归一化问题抛出假设编写图像处理程序读取图像转换为浮点数格式处理后以 8 位颜色保存到磁盘那么整数到浮点数的转换该如何进行呢有标准的除以 255 方法和替代的除以 256 方法用 Python 和 NumPy 实现如下标准的除以 255 方法 | 替代的除以 256 方法---|---pythonpixels img / 255.0result process(pixels)output np.trunc(result * 255 0.5) |pythonpixels (img 0.5) / 256.0result process(pixels)output np.trunc(result * 256)且在最终类型转换之前两种情况下的输出值都会被裁剪python# 裁剪并转换为 8 位output_8bit output.clip(0, 255).astype(np.uint8)标准方法将整数 0 映射到 0.0将 255 映射到 1.0且 GPU 也是这么做的。替代方法增加 0.5 偏移量后除以 256整数 0 被映射到 0.001953125这使得图像处理代码若不知上述常量就无法检测黑色像素逻辑会与 8 位输入绑定而标准方法始终可假设黑色是 0.0。但有些程序员仍倾向于替代方法他们看中了它的什么呢反对除以 255 的理由极端区间较小当在数轴上绘制标准方法时它看起来很奇怪。以将范围在 [0..7] 的 3 位整数映射到 [0, 1] 为例标准公式的极端区间超出了 [0, 1] 的范围虽两种方法都会裁剪输出但它清楚显示了标准范围的“拉伸”这个拉伸后的范围比图像处理中假定的操作范围 [0, 1] 更宽。这意味着将 [0, 1] 范围内的浮点数值转换回整数时极端区间宽度只有其他区间的一半从算法中输出极端值“更难”。例如生成均匀分布的 [0, 1] 噪声并用标准公式四舍五入值 0 和 255 出现频率只有其他整数的一半。通过生成一百万个均匀随机数绘制直方图可验证这一说法突出显示的局部直方图代码pythonimport numpy as npimport matplotlib.pyplot as pltresult np.random.uniform(0, 1, 1000000)final_values np.trunc(result * 255 0.5).clip(0, 255).astype(np.uint8)plt.hist(final_values, bins256, range(0, 255))plt.show()不过很难想出这种远离极端值的偏差会带来问题的例子。标准方法浮点数分布范围更宽但原始图像仍可无损往返转换。而且略大于 0.0 或 1.0 的结果值仍会四舍五入到正确区间使输出分布均匀。例如处理过程从浮点颜色中减去 0.005标准方法中黑色值低于零替代方法中值仍为正但最终两种方法都会输出整数 0plaintext标准方法trunc(255 * (-0.005) 0.5) 0替代方法trunc(256 * (0.5 / 256 - 0.005)) 0所以标准方法中零区间只有“一半大小”并不重要。不精确性标准方法的浮点数值并不精确例如 128 / 255.0 ≈ 0.501961而 128 / 256.0 0.5。由于舍入误差浮点数值之间的距离有微小变化。但这并非真正问题32 位浮点数有 23 位小数部分讨论的是最低有效位的舍入误差抖动幅度小于 2⁻²³。在复杂图像处理任务中0.00001% 的相对误差无关紧要这种不精确性是美学问题而非技术问题。不在整数之间的值替代方法总是将每个浮点数值精确置于两个整数中间这个中间位置可看作折衷因不知原始量化值究竟是多少两个连续整数之间的平均值是不错的猜测。虽很难想出具体应用场景但安德鲁·凯斯勒在 2015 年博客文章《颜色深度转换》中认为抖动处理会更方便理由是添加噪声时无需担心边界情况相比之下标准公式极端情况较棘手需仔细处理才能保持噪声分布的一致性。两种类型的量化器可将这两种方法视为两种不同的均匀标量量化器。从维基百科了解到大多数用于有符号输入数据的均匀量化器分为中升型和中平型。中平型量化器有零值的重建级别中升型量化器有零值的分类阈值。维基百科引用了 1977 年的一篇论文标题和摘要布局独特。当中升型和中平型量化器绘制在图表上时它们在穿过零点的位置不同中平型将零映射到零中升型将零映射到两个整数中间。维基百科选择的符号用 x 表示输入的实数用 k 表示其编码后的整数值用 yₖ 表示重建后的实数。相应的量化器公式如下类型 | 分类编码 | 重建解码---|---|---中升型楼梯量化器 | k trunc(xL) | yₖ (k 0.5) / L中平型楼梯量化器 | k trunc(xL 0.5) | yₖ k / LL 表示不同输出级别的数量例如 256。将这些定义应用到两种竞争方法上标准公式可称为 L 255 的“中升型”替代方法为 L 256 的“中平型”。再次展示代码并加上新标签中升型量化器L 255 | 中平型量化器L 256---|---pythonpixels img / 255.0result process(pixels)output np.trunc(result * 255 0.5) |pythonpixels (img 0.5) / 256.0result process(pixels)output np.trunc(result * 256)从这个角度看标准方法是用于无符号输入的中升型量化器选择 L 255 的整数编码对 8 位输入并非最优这么做是为了编程方便让极端值映射到 0.0 和 1.0。这引出了对标准公式的最后一个批评。更高的量化误差但并非如此若设计系统接收均匀分布的实数 x ∈ [0, 1]编码为 8 位整数 k 再重建为另一个实数 yₖ标准公式会浪费带宽。标准方法中可表示值的范围是 [-0.5 / 255, 255.5 / 255]区间间隔比 [0, 1] 输入严格所需的更大导致更高的重建误差。不过误差增加很小根据 StackOverflow 用户彼得·穆德里耶夫斯基的计算对于除数 255 和 256平均绝对误差分别为 1 / 1020 和 1 / 1024所以除以 256 在理论上更精确。但实际是加载 8 位 RGB 图像处理后再保存无法控制保存时的量化丢失的信息无法恢复。若图像颜色乘以 255 并四舍五入加载时除以 256 不能恢复精度。只有同时控制保存和加载时降低重建误差的诉求才有意义。实际上用替代公式加载他人图像会引入更多误差因为这些图像可能是通过标准公式量化的用错误比例因子解码理论上不正确。在实践中颜色不是绝对测量值只是在稍小范围内处理并带有小偏移量。最后永远不要混合使用两种量化器的编码和解码步骤这很容易犯错。结论如果你处理的是陌生人提供的图像应该将 RGB 值除以 255 进行归一化不精确的浮点数值和更高重建误差的抽象感觉都不是选择替代方法的好理由。但如果你同时控制图像的保存和加载不需要将零映射到零且对将处理代码与 8 位动态范围绑定感到满意那么可以考虑除以 256 以获得更高的精度。只是当同事仍用标准公式加载你的图像时可别抱怨。其他观点乔纳森·布洛在 2002 年的文章中讨论了中升型和中平型量化器但未提及名称本文绘制图表的灵感来源于此。安德鲁·凯斯勒在 2015 年的博客文章中提倡使用替代公式但他将其与未进行四舍五入的标准公式比较使大部分分析无效。作者正在写一本关于颜色缩减算法的书感兴趣可注册。