3

I am trying to use a dicom image and manipulate it using OpenCV in a Python environment. So far I have used the pydicom library to read the dicom(.dcm) image data and using the pixel array attribute to display the picture using OpenCV imshow method. But the output is just a blank window. Here is the snippet of code I am using at this moment.

import numpy as np
import cv2
import pydicom as dicom

ds=dicom.dcmread('sample.dcm')
cv2.imshow('sample image dicom',ds.pixel_array)

cv2.waitkey()

If i print out the array which is used here, the output is different from what i would get with a normal numpy array. I have tried using matplotlib imshow method as well and it was able to display the image with some colour distortions. Is there a way to convert the array into a legible format for OpenCV?

Mushfiq Alam
  • 49
  • 1
  • 1
  • 5
  • 1
    You're missing a call to `cv2.waitKey`. Without it, the GUI doesn't work (read the documentation). – Dan Mašek Dec 10 '18 at 14:49
  • 1
    @DanMašek pardon me, i am in fact using that method but it's not in this snippet. the window does show up but its a blank window. – Mushfiq Alam Dec 10 '18 at 15:15
  • @DanMašek but i'll add it to the code so that people don't misunderstand. – Mushfiq Alam Dec 10 '18 at 15:16
  • What's the data type of the elements of the numpy array you get? `np.int16`? What's the range (min/max value)? Is it single channel? – Dan Mašek Dec 10 '18 at 15:25
  • @DanMašek it is in fact uint16 – Mushfiq Alam Dec 10 '18 at 15:51
  • OK, and what's the maximum value in the array? Do the values span the whole range between 0 and 65535? The reason I'm asking, is that for 16 bit images, `imshow` simply divides everything by 256, in order to display it. If the original image only uses a small part of the whole range, the result will be almost all black (or very dark grays, which are very hard to distinguish by eye). Matplotlib does more complex mapping. – Dan Mašek Dec 10 '18 at 15:59
  • @DanMašek the image is of an MRI slide, so the image itself is greyscale. the values which are currently being shown in the output are only zeros. i assume its because the majority of the image is black. – Mushfiq Alam Dec 11 '18 at 09:58
  • @DanMašek thank you very much. your previous comment helped me understand the problem and i have found a solution. I'm posting it below. – Mushfiq Alam Dec 11 '18 at 14:12

4 Answers4

4

Faced a similar issue. Used exposure.equalize_adapthist() (source). The resulting image isn't a hundred percent to that you would see using a DICOM Viewer but it's the best I was able to get.

import numpy as np
import cv2
import pydicom as dicom
from skimage import exposure

ds=dicom.dcmread('sample.dcm')
dcm_sample=ds.pixel_array
dcm_sample=exposure.equalize_adapthist(dcm_sample)
cv2.imshow('sample image dicom',dcm_sample)

cv2.waitkey()
2

I don't think that is a correct answer. It works for that particular image because most of your pixel values are in the lower range. Check this OpenCV: How to visualize a depth image. It is for c++ but easily adapted to Python.

user2466766
  • 195
  • 1
  • 11
1

I have figured out a way to get the image to show. As Dan mentioned in the comments, the value of the matrix was scaled down and due to the imshow function, the output was too dark for the human eye to differentiate. So, in the end the only thing i needed to do was multiply the entire mat data with 128. The image is showing perfectly now. multiplying the matrix by 255 over exposes the picture and causes certain features to blow. Here is the revised code.

import numpy as np
import cv2
import pydicom as dicom

ds=dicom.dcmread('sample.dcm')
dcm_sample=ds.pixel_array*128
cv2.imshow('sample image dicom',dcm_sample)

cv2.waitkey()
Mushfiq Alam
  • 49
  • 1
  • 1
  • 5
1

This is the best way(in my opinion) to open image in opencv as a numpy array while perserving the image quality:

import numpy as np
import pydicom, os, cv2

def dicom_to_numpy(ds):
    DCM_Img = ds
    rows = DCM_Img.get(0x00280010).value #Get number of rows from tag (0028, 0010)
    cols = DCM_Img.get(0x00280011).value #Get number of cols from tag (0028, 0011)
    
    Instance_Number = int(DCM_Img.get(0x00200013).value) #Get actual slice instance number from tag (0020, 0013)

    Window_Center = int(DCM_Img.get(0x00281050).value) #Get window center from tag (0028, 1050)
    
    Window_Width = int(DCM_Img.get(0x00281051).value) #Get window width from tag (0028, 1051)

    Window_Max = int(Window_Center + Window_Width / 2)
    Window_Min = int(Window_Center - Window_Width / 2)


    if (DCM_Img.get(0x00281052) is None):
        Rescale_Intercept = 0
    else:
        Rescale_Intercept = int(DCM_Img.get(0x00281052).value)

    if (DCM_Img.get(0x00281053) is None):
        Rescale_Slope = 1
    else:
        Rescale_Slope = int(DCM_Img.get(0x00281053).value)

    New_Img = np.zeros((rows, cols), np.uint8)
    Pixels = DCM_Img.pixel_array

    for i in range(0, rows):
        for j in range(0, cols):
            Pix_Val = Pixels[i][j]
            Rescale_Pix_Val = Pix_Val * Rescale_Slope + Rescale_Intercept

            if (Rescale_Pix_Val > Window_Max): #if intensity is greater than max window
                New_Img[i][j] = 255
            elif (Rescale_Pix_Val < Window_Min): #if intensity is less than min window
                New_Img[i][j] = 0
            else:
                New_Img[i][j] = int(((Rescale_Pix_Val - Window_Min) / (Window_Max - Window_Min)) * 255) #Normalize the intensities
                
    return New_Img


file_path = "C:/example.dcm"
image = pydicom.read_file(file_path)
image = dicom_to_numpy(image)

#show image
cv2.imshow('sample image dicom',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Esa Anjum
  • 13
  • 3