• Redkey@programming.dev
    link
    fedilink
    arrow-up
    8
    ·
    edit-2
    16 hours ago

    There’s a whole lot of math in that article, and to be honest I’m not 100% clear on what they’re trying to say. I think they’re talking about how standard quantization isn’t making full or “accurate” use of the number space, although if you read through to the end they note that there’s really nothing terribly wrong with the standard way. But here’s my take.

    To simplify, I’ll use an integer range 0-4 instead of 0-255, but the principle’s the same.

    So, say that we’re quantizing from a floating-point range of 0.0-1.0 to an integer range of 0-4. Doing things the usual way, that’d be the orange numbers on the bottom of this diagram:

    But you can see that this doesn’t line up cleanly with a nice, regular division of the range into 5 equal blocks. If we just multiply by 4 (our integer max) and then round off to the nearest integer, the original value ranges represented by our integer 0 and 4 are half the size of the others. E.g. 0.0 to 0.49 become 0, but 0.5 to 1.49 become 1.

    Now, if we want to represent true 0.0 and 1.0 simply with our integer range (0-4), we should use 0 to exactly equal 0.0, and 4 to exactly equal 1.0. And converting from integer to float by calculating [value / 4] (or [value / 255] in reality) will get us exactly that. I wouldn’t change that.

    What I would suggest changing–to anyone who cares enough about it–is how the numbers are converted from float to integer. If you look at the diagram again, you can see that there is one orange integer per evenly-divided block, even if they don’t line up with the centers of the blocks. If we take a float (0.0-1.0) representation and multiply it by our integer max plus one, we’ll get the blue numbers at the top of the diagram. Then all we have to do is round down to the nearest integer and cap the result at 4 (or 255, or whatever our integer max is), and we’ll have a nice, even distribution in our conversion.

    I could have misunderstood, but it seems like all of the errors and uncertainty discussed in the article come from how the multiplied floats are truncated into integers. I think that this method eliminates that cleanly.

  • [object Object]@lemmy.ca
    link
    fedilink
    arrow-up
    13
    ·
    1 day ago

    The correct way to scale a number from one range to another is ((value - min) / (max - min)) * output_range + output_min and in this way the standard colour normalization should be (value - 0)/(255 - 0) * 1 + 0 = value / 255

    Now the question becomes whether an 8bit pixel value is a correct representation of a colour, or should it quantize to a half value. To answer this, I aggressively point to the above equation. It is fine. There are 256 well considered values, and any system with higher resolutions/precision should also map from 0 to 1 per channel.

    It should be obvious to the reader that 0.5/256 is not zero, and therefore not black. The 256 method is also not idempotent, applying it once destructively alters the data.

    And the argument that 256 has higher resolution: so would 354256, but there’s no sensible place that matters.

    • calcopiritus@lemmy.world
      link
      fedilink
      arrow-up
      3
      ·
      edit-2
      14 hours ago

      The correct way to scale a number from one range to another is …

      Citation needed.

      It is just as valid to quantize the following way:

      q = (x+0.5)/256

      The inverse would be:

      x = min(truncate((q*256)), 255)

      In fact, you could argue that this method is technically more correct. The only issue being the exact value of 1.0, which we have to handle explicitly with min(..., 255).

      This method is idempotent. Try it with x=90 for example. It does destroy data, but that is true for all quantization methods.

      We have to remember that quantization is the process of representing a bigger set of numbers (usually infinite) with a smaller one. We sort a range of values from the bigger set into a single bin in the smaller set.

      The argument of the article is not about “resolution”. The argument is about using the 8bits available as efficiently as possible.

      As the article points out, if you follow the naive approach of:

      q = x/255

      x = round(q * 255)

      You are losing a tiny bit of the 8bit range. Since the ranges represented by 0 and 255 are half as big as the other ones. The naive approach is really quantizing in the range of [0-0.5/255, 1+0.5/255) instead of [0,1). This is because round() maps approaching values to x from both sides, so some negative values map to 0, but there are no negative values in our set.

      The conclusion at the end of the article is mostly the correct one though. If there is an industry standard, it is best to follow that standard. Since mixing a quantization function from one method with the dequantization function from another is just wrong. You can only choose your own method if both your input and your output is the small range.

      EDIT:

      The “naive” approach described in the article does x = truncate(q * 255 + 0.5) which is equivalent to the x = round(q*255)

      • [object Object]@lemmy.ca
        link
        fedilink
        arrow-up
        2
        ·
        11 hours ago

        My idempotent claim was too broad and wrong, up to aliasing that is reversible.

        I think I’ve been talking about this differently than you, when you get 8bit into, 255 in/out is correct.

        When generating data I do it in the natural [0,1] space, then map to whatever domain I’m quantizing for.

        In that case, for N bins you divide by N+1 (and round however you want via correction) so 256. I was probably wrong earlier around this, 256 is correct for quantizing that way.

  • Log in | Sign up@lemmy.world
    link
    fedilink
    arrow-up
    16
    ·
    edit-2
    1 day ago

    This is a stupid debate. Of course you should use 255. Everybody uses 255.

    The standard algorithm means that if you go from int(0-255) to float, black stays black and white stays white. The silly new one turns the extremes very slightly grey.

    This needs stomping on, because inventing a conflicting standard where there’s currently only one is really really stupid.

    If you actually want an even distribution of random colours (who on earth wants that?) then instead of generating random floats, generate random int(0-255). It’s not hard. It really isn’t.

    I bet you that float values that round to 0 come up in float colour data way, way, way more frequently than values that round to 0.5/256, nullifying the silly mathematical point that you might get 0 less often. No. No you won’t. Not in real data.

    • [object Object]@lemmy.ca
      link
      fedilink
      arrow-up
      5
      ·
      1 day ago

      If you’re generating random numbers you apply the correction to your random numbers: [0, 1) is a bin for zero, and [255,256) maps to 255. If you are sampling as such, you are responsible for applying the correction as part of your quantization.

      256 never comes up, because it cannot in the 8bit quantization.

      • Log in | Sign up@lemmy.world
        link
        fedilink
        arrow-up
        5
        ·
        1 day ago

        Yes, indeed. If you’re randomly generating colours and you want to evenly cover the {0,1,…,255} integer space, then generate a random integer in that range directly, or truncate a random float in [0,256). Generating a random float in [0,1) and converting it afterwards, then complaining you’re not getting enough 0 or 255 is silly.

        • calcopiritus@lemmy.world
          link
          fedilink
          arrow-up
          1
          ·
          13 hours ago

          The random number generation is not the source of the complaint. That is just made to easily prove the point in the article.

          The argument is not to use another method when randomly sampling from [0,1). The argument is that f32 color channels are already in the [0,1) range, and following the alternate method results in a “fair-er” representation of the range.

          The plot that was generated with the random sampling is just to show that the standard method is not “fair”.

    • calcopiritus@lemmy.world
      link
      fedilink
      arrow-up
      1
      ·
      edit-2
      7 hours ago

      Did you read the article or just the title?

      I’m honestly surprised this comment is the one that shows at the top.

      • pHr34kY@lemmy.world
        link
        fedilink
        arrow-up
        2
        ·
        edit-2
        6 hours ago

        So, it’s basically saying that anything less than 1/256 (0.00390625f) is 0x0.

        Anything equal to or over 0.99609375 (255/256) should be 0xff.

        Divide by the count (256), not the max index (255). I’m not sure the 0.5 offset is really needed.

        • calcopiritus@lemmy.world
          link
          fedilink
          arrow-up
          1
          ·
          3 hours ago

          The offset is needed because this quantization dividing by 256 uses truncation whereas by 255 uses rounding.

          So a value of 245 (for example) could be anything between 245.00000 and 245.99999. Therefore, when dequantizing, we set the value to 245.5, which is in the middle. Between 245.00000 and 245.99999.

  • Solemarc@lemmy.world
    link
    fedilink
    arrow-up
    32
    arrow-down
    1
    ·
    edit-2
    2 days ago

    My opinion on this will always be that there are only 2 valid answers;

    0-255 or 0-1

    Either a u8 or a float.

    • ferret@sh.itjust.works
      link
      fedilink
      English
      arrow-up
      10
      ·
      2 days ago

      I thought the same thing too, but the article doesn’t disagree. Article is actually about converting between the two. It’s an interesting read, I encourage it.

    • TehPers@beehaw.org
      link
      fedilink
      English
      arrow-up
      3
      ·
      2 days ago

      To answer the question posed in the title: if you’re processing images given to you by strangers, you should normalize RGB values by 255.

      The author isn’t advocating for you to do so. They are just exploring the two approaches. Even in the sentences following this one where they mention where 256 might work, they conclude with your colleagues breaking it all anyway by using 255.