Difference between the functions "im2uint8" (in MATLAB) and "bytescale" (in Python)
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)
2 answers

Firstly, regarding the issue with reading the data, I would suggest using
dcmread
in Python, as that gave me the same exact data asdicomread
in MATLAB.Secondly, in MATLAB when
im2uint8
convertsint16
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 anint16
). Forbytescale
to behave equivalently, I believe you need to set thecmin
andcmax
arguments accordingly (since they will otherwise default todata.min()
anddata.max()
, respectively). This should replicate the results ofim2uint8
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 01 (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