I want to convert a DICOM image from int16 to uint8. I have done it in Python using Z_axis = bytescale(img)
, but this gives different results than using im2uint8
in MATLAB. In MATLAB, The minimum and maximum values of a DICOM image after converting to uint8 using im2uint8
are (124, 136), respectively. But these values in Python after converting using bytescale
are (0, 255).
Python code:
for person in range(0, len(dirs1)):
if not os.path.exists(os.path.join(directory, dirs1[person])):
Pathnew = os.path.join(directory, dirs1[person])
os.makedirs(Pathnew)
for root, dirs, files in os.walk(os.path.join(path, dirs1[person])):
dcmfiles = [_ for _ in files if _.endswith('.dcm')]
for dcmfile in dcmfiles:
dcm_image = pydicom.read_file(os.path.join(root, dcmfile))
img = dcm_image.pixel_array
Z_axis = bytescale(img)
minVal = Z_axis.min()
maxVal = Z_axis.max()
Matlab code:
for j = 1 : length(Files2)
img = dicomread([galleryPath Files2(j).name]);
Z_axis = im2uint8(img);
minVal = min(min(Z_axis));
maxVal = max(max(Z_axis));
The images look equal when displayed, but the numeric values are not. So, are the bytescale
and im2uint8
functions equal or not? If not, I want results like im2uint8
in Python. What kind of function should I choose (especially for DICOM images)?
For example, in MATLAB after reading a DICOM file:
img = dicomread([galleryPath Files2(j).name]);
img = [ -1024, -1024, 16;
-1024, 8, 11;
17, 5, 8];
But in Python, the same image after reading is:
dcm_image = pydicom.read_file(os.path.join(root, dcmfile))
img = dcm_image.pixel_array
img = array([[ -1024, -1024, 27],
[ -1024, 27, 26],
[ 24, 26, 23]])
I don't know why they are different in MATLAB and Python. After applying im2uint8
in MATLAB, the output is:
Z_axis = im2uint8(img)
Z_axis =
3×3 uint8 matrix
124 124 128
124 128 128
128 128 128
And after applying bytescale
in Python, the output is:
bytescale(img)
Z_axis =
array([[0, 0, 83],
[0, 83, 83],
[83, 83, 83]], dtype=uint8)
Firstly, regarding the issue with reading the data, I would suggest using dcmread
in Python, as that gave me the same exact data as dicomread
in MATLAB.
Secondly, in MATLAB when im2uint8
converts int16
values it will scale them assuming minimum and maximum values for the data equal to -32768 and 32767, respectively (i.e. the minimum and maximum values representable by an int16
). For bytescale
to behave equivalently, I believe you need to set the cmin
and cmax
arguments accordingly (since they will otherwise default to data.min()
and data.max()
, respectively). This should replicate the results of im2uint8
in Python:
Z_axis = bytescale(img.astype(float), cmin=-32768, cmax=32767)
Note: conversion of the data to float first is necessary to account for an apparent bug in bytescale
that doesn't handle the integer arithmetic properly (found courtesy of Cris Luengo).
Yes it's different
bytescale
convert matrix to uint8 normalizing all values by the the highest and the lowest (highest 255 and lowest 0)
img = array([[ 91.06794177, 3.39058326, 84.4221549 ],
[ 73.88003259, 80.91433048, 4.88878881],
[ 51.53875334, 34.45808177, 27.5873488 ]])
bytescale(img)
array([[255, 0, 236],
[205, 225, 4],
[140, 90, 70]], dtype=uint8)
In Matlab im2uint8
it's useful to convert double images to uint8. Double images has range 0 to 1.
To do the same in Matlab fist you need to convert image in range of value 0-1 (double) normalizing the data, then apply im2uint8.
I = [ 91.06794177, 3.39058326, 84.4221549;
73.88003259, 80.91433048, 4.88878881;
51.53875334, 34.45808177, 27.5873488];
Inorm = (I - min(I(:))) ./ ( max(I(:)) - min(I(:)) );
I2 = im2uint8(Inorm);
Then you obtain the same image:
I2 =
3×3 uint8 matrix
255 0 236
205 225 4
140 90 70
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With