Thursday, September 27, 2012

Signed, Unsigned pixel values



Among the various image types proposed to import a gray-level image, even though you know the dynamic range of your image, you have to choose 'signed' or 'unsigned'...


In this series Import a raw image, let's see another option available in the Import dialog box: signed or unsigned numbers as shown in Fig.1. 

Fig.1: Several image types using (un)signed numbers are available in the Import dialog box.
1- A 'bit' of theory

Java (the programming language of ImageJ and its virtual processor, the JVM) encodes signed numbers with the method called  "two’s complement" [Wiki] and [Wiki].
The last bit (the most significant) is used for the sign (a value of 0 for positive and 1 for negative). For positive 16-bit numbers, no extra process are done, the bits #0 to #15 are used allowing the encoding of numbers from 0 (00000000 000000002) to +32767 (01111111 111111112). For negative numbers, a small calculation is done consisting of a bits inversion and an addition of +1 as shown in the following example ...

Example #1: -32767
+3276710 = 01111111 111111112
inversion : 10000000 000000002
addition + 1 : 10000000 000000012 = -3276710

Example #1: -1
110 = 00000000 000000012
11111111 111111102
11111111 111111112= -1

 Thus, signed 16-bit numbers are comprised in the range of [-32767;+32767] whereas unsigned 16-bit numbers between [0;65535].

2- Consequences for gray-level images

Now, we've seen the theory, what about gray-level images ?

If you import an image with the wrong 'sign', you get image with inverted areas as shown in Fig.2 (right panel). 

Fig.2: 16-bits image of Lena imported as unsigned (left) and signed (right).

Let's go further. Create an 16-bit image 512x50 with a ramp background (File > New > Image...) as shown in Fig. 3. If you look at the pixel values by hovering the image, values are comprised between 0 and 65535 (ImageJ always creates unsigned 16-bit image).

Fig. 3: Ramp image. In unsigned 16-bit  type, black is 0 and white 65535. In signed 16-bit, black is -32767 and white +32767. 0 (zero) is located at the middle of the ramp and corresponds to a medium gray.

If you save this ramp as a raw (16-bit unsigned, by default) image and import it as a '16-bit signed', you'll get the image of Fig. 4. What happens?

Fig.4: Ramp image saved as an unsigned 16-bit and imported as a signed 16-bit. The grays seem split in two parts.

Just look at some key numbers in the ramp image...

In unsigned 16-bit,
00000000 000000002 = 010
. . . . . .
01111111 111111112 = 3276710
10000000 000000002 = 3276810
10000000 000000012 = 3276910
. . . . . .
11111111 111111112 = 6553510

In signed 16-bit,

00000000 000000002 = 010
. . . . . .
01111111 111111112 = +3276710
10000000 000000012 = -3276710
. . . . . .
11111111 111111112 = -110

Now, if we compare the encoding of the signed and unsigned numbers, they are identical from 0 to +32767, but from +32768 to + 65535, depending of the encoding, they appear as negative (or positive) numbers (e.g. signed -110 has the same encoding as the unsigned 6553510). That explains the strange pattern of the ramp of Fig. 4. The left part (medium gray to white) corresponds to the range [0;+32767] whereas the right part corresponds to negative numbers [-32767;-1].

To fix this problem, convert the 'wrong signed' image in floating-point numbers (Image > Type > 32-bit) and use the function Process > Math > Macro... to shift the negative values with the following formula:
if (v < 0) v=v+65536
Finally, convert again the fixed image to 16-bit.


3 - What about signed or unsigned 8-bit image?

In ImageJ, the only option called '8-bit' corresponds to unsigned 8-bit numbers (from 0 to 255) and there is no way to import a signed 8-bit image (-127 to +127). How can we deal with that?

Import the image as a 8-bit. Then the tricky part is to convert the image in floating-point numbers to make the calculation. Subtract 256 to all the pixels greater than 127 and finally change this fixed image to 8-bit. Automatically, the pixel values are shifted between 0 and 255.

if (v > 127) v=v-256
Your pixel values are back!!
Note: To create a signed 16-bit image, I use this formula '[if (v <= 32767) v+=32767; else v-=32767;]' and save it as a raw image (File > Save As > Raw Data...).

4- Links

  • Series of posts Image File [TOC]
  • Crazybiocomputing mini-games levels #8 and 9 [Link]


No comments:

Post a Comment