Converting 24-Bit Numbers to 8-Bit Numbers

QUESTION: I have a 24-bit number, 14772545, which represents a royal blue color. But in my particular application, I need to decomposed this into three 8-bit numbers representing the RBG values buried in the number. How can I recover these RGB values in IDL?

ANSWER: Let's consider a royal blue color, which is represented by the RGB triple (65, 105, 225) where the red value is 65, the green value is 105, and the blue value is 225. In IDL, we can create a 24-bit value from these numbers like this:

   IDL> num24 = 65 + (105 * 2L^8) + (225 * 2L^16)
   IDL> Print, num24
          14772545

What you are asking to do is to go in the reverse direction. To see how this can be done, it might be useful to see a binary representation of the 24-bit number. We can use BINARY to do so, like this:

   IDL> Print, Binary(num24, /COLOR, /SEPARATE)
           1 1 1 0 0 0 0 1    0 1 1 0 1 0 0 1    0 1 0 0 0 0 0 1

In this representation, the 8 bits that make up the blue color are on the left, and the 8 bits that make up the red color are on the right. What we wish to know, essentially, is what number (byte value) is associated with each of these three separate bit patterns. In other words, which number is associated with the bit pattern 1 1 1 0 0 0 0 1 and represents a blue color?

To solve the problem, we need a way of separating these three bits patterns out of the 24-bit number. The bitwise operator AND will be of some use to us, if we can determine which number to AND against. Since each bit represents a power of two, if we wanted to find a number to AND with our color, we want a number that has bits 17 through 24 set. In other words, we want this number:

   1 1 1 1 1 1 1 1    0 0 0 0 0 0 0 0    0 0 0 0 0 0 0 0

Since,

   1 1 1 0 0 0 0 1    0 1 1 0 1 0 0 1    0 1 0 0 0 0 0 1
                          AND
   1 1 1 1 1 1 1 1    0 0 0 0 0 0 0 0    0 0 0 0 0 0 0 0 0
                          EQUALS
   1 1 1 0 0 0 0 1    0 0 0 0 0 0 0 0    0 0 0 0 0 0 0 0 0

We can find this number in IDL like this:

   IDL> numberToAndBlue = Long(Total(2L^(Lindgen(8)+16)))
   IDL> Print, numberToAndBlue
           16711680
   IDL> Print, Binary(numberToAndBlue, /COLOR, /SEPARATE)
           1 1 1 1 1 1 1 1    0 0 0 0 0 0 0 0    0 0 0 0 0 0 0 0

(Another way to find this number, if you remember back to your hexadecimal days, is like this: numberToAndBlue = 'FF0000'xL.)

And, so:

   IDL> blueBits = num24 AND numberToAndBlue
   IDL> Print, Binary(blueBits, /COLOR, /SEPARATE)
           1 1 1 0 0 0 0 1    0 0 0 0 0 0 0 0    0 0 0 0 0 0 0 0
   IDL> Print, bluebits
           14745600

Now, of course, that is not exactly what we want. We have the blue bits separated out, but we want to know what 8-bit value (byte value) is represented by those bits. Another way of saying this is we need to shift that bit pattern 16 bits to the right, and then determine what the number is. We can do this with the ISHFT command in IDL.

   IDL> blue = ISHFT(bluebits, -16)
   IDL> Print, Binary(blue, /COLOR, /SEPARATE)
           0 0 0 0 0 0 0 0    0 0 0 0 0 0 0 0    1 1 1 0 0 0 0 1
   IDL> Print, blue
           225

Perfect!

So, a general algorithm for converting a 24-bit number to three eight bit numbers would look something like this.

   IDL> num24 = 14772545L
   IDL> numberToAndBlue = Long(Total(2L^(Lindgen(8)+16))) 
   IDL> numberToAndGreen = Long(Total(2L^(Lindgen(8)+8))) 
   IDL> numberToAndRed = Long(Total(2L^(Lindgen(8)))) 
   IDL> blue = ISHFT(num24 AND numberToAndBlue, -16)
   IDL> green = ISHFT(num24 AND numberToAndGreen, -8)
   IDL> red = num24 AND numberToAndRed
   IDL> Print, red, green, blue
          65         105         225

Or, in a shorter version.

   IDL> num24 = 14772545L
   IDL> blue = ISHFT(num24 AND 'FF0000'xL, -16)
   IDL> green = ISHFT(num24 AND '00FF00'xL, -8)
   IDL> red = num24 AND '0000FF'xL
   IDL> Print, red, green, blue
          65         105         225

Or (as Med Bennett points out to me), in the shortest version yet. (Which, I confess, I don't completely understand.)

   IDL> num24 = 14772545L
   IDL> Print, [num24 MOD 2L^8, (num24 MOD 2L^16)/2L^8, num24/2L^16]
          65         105         225

I'll leave it as an exercise for the reader to write a Convert24to8 function.

Google
 
Web Coyote's Guide to IDL Programming