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

  • answered 2018-10-09 17:11 gnovice

    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).

  • answered 2018-10-09 17:12 pfRodenas

    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