5

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)
gnovice
  • 125,304
  • 15
  • 256
  • 359
Ellie
  • 303
  • 2
  • 16

2 Answers2

4

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

gnovice
  • 125,304
  • 15
  • 256
  • 359
4

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
pfRodenas
  • 326
  • 1
  • 2
  • 12
  • I want to use "im2uint8" in python. what kind of function should I choose? (especially for dicom images) – Ellie Oct 09 '18 at 17:47
  • Can you comment a sample of a image values like I did in my comment. – pfRodenas Oct 09 '18 at 17:55
  • How can I send you a dicom file? – Ellie Oct 09 '18 at 18:30
  • No, only a 3x3 matrix with values, to see the datatype – pfRodenas Oct 09 '18 at 18:33
  • after applying `im2uint8` in your img matrix my result in Matlab is `img8 = 3×3 uint8 matrix 0 0 255 0 255 255 255 255 255` – pfRodenas Oct 09 '18 at 19:55
  • In your code, it is used as Z-axis = im2uint8(double(img)) that the output image is not acceptable; but my image matrix is an int16 (dicom), that's mean: Z-axis = im2uint8(int16(img)) or Z-axis = im2uint8(dicom image). – Ellie Oct 09 '18 at 20:22
  • I don't know the reason why in my code the output images in Python and Matlab are the same, but their min and max values are different! – Ellie Oct 09 '18 at 20:36
  • `img = np.array([[ -1024, -1024, 27], [ -1024, 27, 26], [ 24, 26, 23]])` `Z_axis = bytescale(img.astype(float), cmin=-32768, cmax=32767) Z_axis=array([[124, 124, 128], [124, 128, 128], [128, 128, 128]], dtype=uint8)` – pfRodenas Oct 09 '18 at 20:37
  • Yes, Now, They are the same. Thank you for replying. – Ellie Oct 09 '18 at 20:45
  • If that answer is correct it would be good to mark it as such :) @eli – g_uint Oct 10 '18 at 08:19