Imadjust fails with single or double data type

5 views (last 30 days)
I try to use imadjust on an image which has single (or double) datatype. But this does not seem to work. Neither does the function returns a warning or an error.
An example, modified from the documentation:
I = imread('pout.tif');
imshow(I)
J = imadjust(single(I));
figure
imshow(J,[])
I just added the single conversion on I, and afterwards the contrast is not computed correctly.

Answers (1)

Walter Roberson
Walter Roberson on 30 Dec 2021
imadjust() works as it is designed.
When your data is single() or double(), MATLAB interprets the value 0.0 as being as dark as possible, and 1.0 as being as intense as possible. When it comes to computation or displays, the calculation routines generally treat values outside of that range as if they were at the extreme end of the valid range. So -5.7 would be treated as 0.0 and 173.0 would be treated as 1.0 .
When you single() a uint8 image, the range of values is not change. The uint8(173) becomes 173.0 and so on: same numeric value, different representation. But when the image is uint8 then pretty much all of the image is likely to be 1.0 or greater and so be treated as being full intensity.
You should not use single() or double() on a uint8 image unless you are aware of this fact and take it into account in your coding.
Instead you should be using functions such as im2double() or im2single() . Those functions will look at the data type of the input and will map the range of values appropriate for that data type, into the range of values expected for double() or single() images.
  2 Comments
Markus Schmidt
Markus Schmidt on 30 Dec 2021
Dear Walter,
Thank you for your detailed response.
Still, I am not satisfied with the behavour of imadjust. If for single and double the expected value range is [0,1], then is see some possibilities
  1. This should be documented in the function
  2. This function should raise a warning if the input is not in this range
  3. The function could scale the values internally for the algorithm to work.
In our project, we post-process a series of images. Especially addition and subtraction is not feasible with integers. Furthermore, we want to preserve the original values for comparison with the initial raw values.
Walter Roberson
Walter Roberson on 31 Dec 2021
An RGB MATLAB® array can be of class double, uint8, or uint16. In an RGB array
of class double, each color component is a value between 0 and 1. A pixel whose
color components are (0,0,0) is displayed as black, and a pixel whose color
components are (1,1,1) is displayed as white.
That is fundamental for every function that works with images.
You can file a bug report with Mathworks to have them add the information to the imadjust documentation specifically, but personally I would not expect them to bother.
The documentation for imadjust() specifies https://www.mathworks.com/help/images/ref/imadjust.html#budqw0o-1-I that the first parameter, is a grayscale "image" or RGB "image". When you pass in floating point values that are outside the range 0 to 1, that is not a valid image: you have violated the documented interface, and MATLAB can do anything it likes with the input.
"The function could scale the values internally for the algorithm to work."
Could it?
Suppose the input values are in the range -3140.0 to +11782, then how "should" MATLAB scale that? Such values can be produced by single() or double() of an int16 image. Should MATLAB treat -3140.0 as "black" and +111782.0 as "white" ? No !! If you have that range of values then you very likely have a CT scan, and negative values reflect portion of water, with -4000.0 often representing water saturation. So -3140.0 as a reading would not represent water saturation, it would reflect something not fully saturated, similar to a grayscale image that does not happen to have any pure black in it.
Now, technically speaking, you could get a reading as low as -12000 Hounsfield Units... it is just really rare to see anything lower than -4000 HU. But in theory -12000 is the base point, so if you see a -3140.0 then instead of saying that it is "almost completely black", you should in theory be considering it as being about 37% white.
Okay, now suppose the function receives an image in the range +860.0 to +11782.0 . So... 0 to +12000 ? Must be another CT image that already had water suppressed (not an uncommon occurance) and the +11782 must be just under full-intensity of +12000, right? So it should be scaled as if the full range was -12000 to +12000 ? Or as if the full range was -4000 to +12000? Or if the full range was 0 to +12000 ?
Hah, trick question! CT scans are often also represented as uint16 images with a shift of 4000 HU, so a reading of X in the file is (X-4000) HU, so 860.0 might correspond to -3140.0 HU and +11782.0 might correspond to +7782.0 HU (about 65% bright instead of the 98% bright when that +11782.0 arose from an int16 CT scan). And it just so happens that the useful range of unshifted (int16) CT scans with water supression already done overlaps with the useful range of shifted (uint16) CT scans with water suppression not already done, so you can't tell just by looking at the range of values !!
Any automatic rescalling to 0.0 to 1.0 done by the function would be in error for some range of data.
In our project, we post-process a series of images. Especially addition and
subtraction is not feasible with integers. Furthermore, we want to preserve
the original values for comparison with the initial raw values.
Well, the way to do that is to im2double() when you read the images, do your processing, and then afterwards im2uint8() or im2uint16() or as appropriate to match the original data range.

Sign in to comment.

Products


Release

R2021b

Community Treasure Hunt

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

Start Hunting!