代码之家  ›  专栏  ›  技术社区  ›  inkredibl

将颜色值从浮点0..1转换为字节0..255

  •  14
  • inkredibl  · 技术社区  · 14 年前

    将颜色值从浮点转换为字节的正确方法是什么?起初我想 b=f*255.0 应该这样做,但现在我想,在这种情况下,只有 1.0 将转换为 255 但是 0.9999 将已经 254 这可能不是我想要的…

    似乎 b=f*256.0 会更好,除非它会有一个不想要的案件 256 如果准确的话 .

    最后我用这个:

    #define F2B(f) ((f) >= 1.0 ? 255 : (int)((f)*256.0))
    
    6 回复  |  直到 7 年前
        1
  •  21
  •   ToolmakerSteve    7 年前

    1.0是唯一可能出错的情况,因此请单独处理该情况:

    b = floor(f >= 1.0 ? 255 : f * 256.0)
    

    此外,为了避免舍入错误导致的错误行为(例如f=1.0000001),可能需要强制f为0<=f<=1。

    f2 = max(0.0, min(1.0, f))
    b = floor(f2 == 1.0 ? 255 : f2 * 256.0)
    

    替代安全解决方案:

    b = (f >= 1.0 ? 255 : (f <= 0.0 ? 0 : (int)floor(f * 256.0)))
    

    b = max(0, min(255, (int)floor(f * 256.0)))
    
        2
  •  7
  •   Erich Kitzmueller    14 年前

    为什么不试试

    b=f*255.999
    

    摆脱特殊情况 f==1 但0.999仍然是255

        3
  •  7
  •   ToolmakerSteve    7 年前

    我总是这样 round(f * 255.0) .

    无需测试(1的特殊情况)和/或夹持其他答案。对于您来说,这是否是一个理想的答案取决于您的目标是尽可能地匹配输入值[我的公式],还是将每个组件划分为256个相等的间隔[其他公式]。

    我公式的可能缺点是0和255区间的宽度只有其他区间的一半。经过多年的使用,我还没有看到任何不良的视觉证据。相反,我发现最好在输入非常接近它之前不要达到任何极端——但这是一个品味问题。

    可能的好处是 相对的 R-G-B分量的值(稍微)更精确,用于更大范围的输入值。
    虽然我还没有尝试证明这一点,但这是我的直觉,因为对于每个组件,我都会四舍五入以得到最接近的可用整数。(例如,我认为,如果一种颜色的g~=2x r,这个公式将更经常保持接近这个比率;尽管差别很小,而且还有许多其他颜色 256 公式做得更好。所以这可能是一场洗礼。)

    实际上,或者 二百五十六 255 -基于方法似乎提供了良好的结果。


    另一种评估方法 二百五十五 VS 二百五十六 ,是为了检查 其他 方向-
    从0..255字节转换为0.0..1.0浮点。

    将0..255整数值转换为0.0..1.0范围内等距值的公式为:

    f = b / 255.0
    

    朝这个方向走,就不存在是否使用的问题。 二百五十五 256个 :上述公式 产生等距结果的公式。注意它的用途 二百五十五 .

    了解 二百五十五 公式在两个方向上,考虑这个图表,如果您只有2位,那么整数值为0..3:

    图使用 3 对于两位,类似于 二百五十五 8位。转换可以是从上到下或从下到上:

    0 --|-- 1 --|-- 2 --|-- 3  
    0 --|--1/3--|--2/3--|-- 0
       1/6     1/2     5/6
    

    这个 | 是4个范围之间的界限。请注意,在内部,浮点值和整数值位于其范围的中点。注意 间距 在两种表示中,所有值之间都是常量。

    如果你掌握了这些图表,你就会明白我为什么喜欢 二百五十五 -基于公式 二百五十六 -基于公式。


    索赔 如果你使用 / 255.0 去的时候 要浮动的字节,但不使用 圆形(F*255.0) 当去 来自浮点的字节, 然后“平均往返”误差增加 . 详情如下。

    最容易测量的方法是从float开始,转到byte,然后返回float。要进行简单分析,请使用2位“0..3”图。

    从大量的浮点值开始,平均分布在0.0到1.0之间。 往返行程将所有这些值分组到 4 价值观。
    该图有6个半间隔长度范围:
    0..1/6,1/6..1/3..5/6..1
    对于每个范围,平均往返误差是范围的一半,所以 1/12 (最小误差为零,最大误差为1/6,均匀分布)。
    所有的范围都给出了相同的误差; 1/12 是往返时的总平均误差。

    如果您使用 * 256 * 255.999 公式, 往返的结果是相同的,但有一些被移到了邻近的范围。
    对另一个范围的任何更改都会增加错误 ;例如,如果以前单个浮点输入的错误是轻微的 较少的 大于1/6时,返回相邻范围的中心会导致轻微的错误 更多 超过1/6。例如,最佳公式中的0.18=>字节1=>浮点1/3 ~=0.333,表示错误| 0.33-0.18| = 0.147 使用 二百五十六 formula=>byte 0=>float 0,表示错误 0.18 ,这是最佳误差的增加 零点一四七 .

    图表使用 * 4 具有 / 3 . 转换是从一行转换到下一行。
    注意第一行的间距不均匀:0..3/8、3/8..5/8、5/8..1。这些距离是3/8、2/8、3/8。 注意最后一行的间隔边界与第一行不同。

       0------|--3/8--|--5/8--|------0
             1/4     1/2     3/4
    => 0------|-- 1 --|-- 2 --|------3  
    
    => 0----|---1/3---|---2/3---|----0
           1/6       1/2       5/6
    

    避免这种增加的错误的唯一方法是在从字节到浮点转换时使用一些不同的公式。如果你坚信 二百五十六 公式,那么我就留给你来决定最佳逆公式。
    (对于每个字节值,它应该返回成为该字节值的浮点值的中点。0到0,3到1除外。或者0到1/8,3到7/8!在上面的图表中,它应该将您从中间一行带回到顶部一行。)

    但是现在您将很难防御这样的情况,即您采用了等间距的字节值,并将它们转换为非等间距的浮点值。

    如果您使用的值不是 二百五十五 ,对于整数0..255:平均往返误差增加,或浮点域中的非均匀间隔值。

        4
  •  1
  •   Regular Guy    12 年前

    接受的解决方案在比较float时失败,因为它是整数。

    这个代码很好用:

    float f;
    uint8_t i;
    //byte to float
    f =CLAMP(((float)((i &0x0000ff))) /255.0, 0.0, 1.0);
    //float to byte
    i =((uint8_t)(255.0f *CLAMP(f, 0.0, 1.0)));
    

    如果没有夹子:

    #define CLAMP(value, min, max) (((value) >(max)) ? (max) : (((value) <(min)) ? (min) : (value)))
    

    或者对于完整的RGB:

    integer_color =((uint8_t)(255.0f *CLAMP(float_color.r, 0.0, 1.0)) <<16) |
                   ((uint8_t)(255.0f *CLAMP(float_color.g, 0.0, 1.0)) <<8) |
                   ((uint8_t)(255.0f *CLAMP(float_color.b, 0.0, 1.0))) & 0xffffff;
    
    float_color.r =CLAMP(((float)((integer_color &0xff0000) >>16)) /255.0, 0.0, 1.0);
    float_color.g =CLAMP(((float)((integer_color &0x00ff00) >>8)) /255.0, 0.0, 1.0);
    float_color.b =CLAMP(((float)((integer_color &0x0000ff))) /255.0, 0.0, 1.0);
    
        5
  •  0
  •   Pavel Radzivilovsky    14 年前

    我相信正确的是地板(F*256),不是圆形的。这将把间隔0..1映射到正好256个相等长度的区域。

    [编辑]特殊情况下选择256。

        6
  •  0
  •   jonsca    12 年前
    public static void floatToByte(float f)
    {
         return (byte)(f * 255 % 256)
    }
    

    值<1被精确转换。

    在转换后,介于255和256之间的值在转换为字节时会被转换为255。

    使用 % 操作员。