convert image from double to uint

5 views (last 30 days)
André
André on 25 Mar 2024
Commented: André on 27 Mar 2024
I have an image, lets say:
I = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
I want to convert this image to uint8 and uint2.
uint8 is easy, I just use im2uint8.
The formula MATLAB uses, I believe is:
I_uint8 = round(I * (2^8 - 1))
Because there is no function to convert to uint2, I did the same approach:
I_uint2 = round(I * (2^2 - 1))
I obtain the following result:
0 0 1 1 1 2 2 2 2 3 3
This is clearly wrong, because all double values in [0, 0.25[ should be mapped to 0, all values in [0.25, 0.5[ to 1, all values in [0.5, 0.75[ to 2 and all values in [0.75, 1] to 3. So the right approach should be:
I_uint2_correct = min(2^2 - 1, floor(I * 2^2))
So I used the floor function instead of round, and did not subtract 1. The min function changes the maximum value from 4 to 3.
The output is:
0 0 0 1 1 2 2 2 3 3 3
Everything is correct now.
By analogy, I conclude the im2uint8 funcion uses the wrong procedure as well. For example:
im2uint8(0.1) % same as round(0.1*255)
min(2^8 - 1, floor(0.1 * 2^8))
The output is:
ans =
26
ans =
25
I know for 8 bits the differences are insignificant, and nobody cares. But for 2, 3 or 4 bits, this is important.
And to prove this imprecision, lets do the following:
figure
imshow(0.1)
figure
imshow(im2uint8(0.1))
By using the data cursor on the first image, I obtain a RGB intensity of 0.0980392. In the second, I obtain 1.101961.
So, same image, different RGB values.
If I search for 0.0980392 in a gray(256) colormap, I find it at row 26 of the array, which corresponds to index 25 (because MATLAB starts counting at 1, not 0). On the other hand, the 1.101961 is at row 27, which corresponds to index 26. This is in agreement with the results above.
So I ask, why does im2uint8 use a procedure that is not totally precise? What are the advantages?
And why does MATLAB not compensate for this when plotting the images? This may be relevant for applications such as visual stimulation.
  4 Comments
DGM
DGM on 26 Mar 2024
nlevels = 8;
inrange = [0 1];
% for the first case
binw = diff(inrange)/(nlevels-1);
edges1 = linspace(inrange(1)-binw/2,inrange(2)+binw/2,nlevels+1)
edges1 = 1x9
-0.0714 0.0714 0.2143 0.3571 0.5000 0.6429 0.7857 0.9286 1.0714
% for the second case
edges2 = linspace(inrange(1),inrange(2),nlevels+1)
edges2 = 1x9
0 0.1250 0.2500 0.3750 0.5000 0.6250 0.7500 0.8750 1.0000
For op1, the first three bin edges are approximately [-0.07143 0.07143 0.2143]. The widths of these two bins are both 0.1429, but since our input is constrained at 0, we're only ever actually using half of the first (and last) bin.
This is merely an observation on my part, not a statement of propriety.
André
André on 27 Mar 2024
Now I understand what you mean. So the first and last been are truncated at 0 and 1 respectively. When you say bins are centered on input limits, the centers are the 0 and 1 values. We do not see the other half becase they are out of range.
So you say in the first approach what matters are the bin centers. The bin centers are actually the colormap values, which can be obtained by gray(8).
Of course, method 1 allows the colormap to include the 0 and 1 values, while method 2 allows to have bins with the same length. It is not possible to have both at the same time, which would be the ideal situation.
I have another 2 questions here:
1 - Can your also confirm that imagecs uses op1, while imshow uses op2? Because imagesc(127) and imshow(127) with a gray(256) colormap have different indexes on the colormap.
2 - if I apply a gray(8) colormap to your previous plot with 8 levels, none of the RGB values of the data cursor match the entries of gray(8). But they match entries of gray(256). Does this mean that MATLAB plotting functions always use entries of the standard 8 bit colormap, regardless of the colormap size we choose?

Sign in to comment.

Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!