.dcm support in fastai v1

Does fastai v1 support .dcm file read? Thanks!

2 Likes

Hi, fast.ai is a library for accelerate Deep Learning. In case of .dcm (DICOM) is a standard, so it is implemented in medical packages but compatible with python.

1 Like

Looks like .dcm is not yet supported, i got the following error when i tried creating ImageDataBunch

OSError: Traceback (most recent call last):
  File "/opt/local/miniconda/envs/dl/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 138, in _worker_loop
    samples = collate_fn([dataset[i] for i in batch_indices])
  File "/opt/local/miniconda/envs/dl/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 138, in <listcomp>
    samples = collate_fn([dataset[i] for i in batch_indices])
  File "/opt/local/miniconda/envs/dl/lib/python3.7/site-packages/fastai/vision/data.py", line 73, in __getitem__
    def __getitem__(self,i): return open_image(self.x[i]),self.y[i]
  File "/opt/local/miniconda/envs/dl/lib/python3.7/site-packages/fastai/vision/image.py", line 355, in open_image
    x = PIL.Image.open(fn).convert('RGB')
  File "/opt/local/miniconda/envs/dl/lib/python3.7/site-packages/PIL/Image.py", line 2657, in open
    % (filename if filename else fp))
OSError: cannot identify image file '/opt/local/external/rsna/data/train/e59728f7-a4ac-47d3-9962-429bcdb579ef.dcm'

i’m looking for work around to use fast v1 with .dcm files.

1 Like

Can you share your code and data ?

data is based off of rsna pneumonia detection challenge kaggle competition.

I tried to create a labels.csv file using only the image name and target variable, and then creation of ImageDataBunch, ConvLearner object and finally fit.

data = vision.ImageDataBunch.from_csv(path, folder='train', csv_labels='labels.csv', valid_pct=0.2, suffix='.dcm')
learn = vision.ConvLearner(data, vision.models.resnet18, metrics=accuracy)
learn.fit(1)

@snmateen, I don’t know how important it is to keep the original format, but if it is not, perhaps you could just convert them?

mogrify -format JPEG *

and then use fastai normally?

mogrify is from imagemagick. it can handle pretty much any image format - of course choose the destination format that is not lossy, JPEG was just an example.

Check the source code for the various ImageDataset classes. It’s pretty easy to create a DICOM version from that.

Thanks @jeremy, i’m trying to replace the open_image function with the below one.

from fastai.vision import Image
from fastai.vision import pil2tensor

def open_dcm_image(fn)->Image:
    "Return `Image` object created from image in file `fn`."
    array = pydicom.dcmread(fn).pixel_array
    x = PIL.Image.fromarray(array).convert('RGB')
    return Image(pil2tensor(x).float().div_(255))

fastai.vision.data.open_image = open_dcm_image

followed by

data = ImageDataBunch.from_csv(path, 
                              folder='train', 
                              csv_labels='labels.csv', 
                              valid_pct=0.2, suffix='.dcm')
learn = ConvLearner(data, models.resnet18, metrics=accuracy)
learn.fit(1)

i’m i doing it right?

3 Likes

Just look at data.train_ds[0] to see what it looks like. You might also check it loads batches properly by running next(iter(data.train_dl)) or data.show_batch().

@sgugger, yes it worked great! Thank you!

few more questions on similar topic, is resnet[35,50] a good architecture for medical images such as X-rays?

can anyone suggest how to predict bounding boxes on X-rays?

2 Likes

Did it work? If so, how much did it slow down because of the conversion overhead?

Often for CT images, the pixels are uint16 which needs to be converted to uint8 while preserving the information. If you do a direct convert, the body will be completely white and most of the detail is lost. Four variables for converting the image are Intercept, Slope, window, and level. You can grab the first 2 from the dicom file. The second two depend on the body part of interest. For abdomen, window 350 and level 40 is a good start.

def open_dcm_image(fn)->Image:
   dicom_file = pydicom.dcmread(str(fn))
 
   arr = dicom_file.pixel_array.copy() 
   arr = arr + int(dicom_file.RescaleIntercept) 
   arr = arr * int(dicom_file.RescaleSlope) 
   level = 40; window = 350
   arr = np.clip(arr, level - window / 2, level + window / 2)
 
   x = pil2tensor(PIL.Image.fromarray(arr).convert("RGB"), np.float32).div_(255)
   return Image(x)
3 Likes

Thanks astein for pointing out that pixel values need to be converted. However, slight change in the code based on following link http://www.idlcoyote.com/fileio_tips/hounsfield.html

# it should be 
arr =  arr * int(dicom_file.RescaleSlope)  + int(dicom_file.RescaleIntercept)

Posting the entire code

def open_dcm_image(fn:PathOrStr, div:bool=True, convert_mode:str='RGB', cls:type=Image,
        after_open:Callable=None)->Image:
    "Return `Image` object created from image in file `fn`."
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", UserWarning) # EXIF warning from TiffPlugin
        #x= PIL.Image.open(fn).convert(convert_mode)
        # code added for opening dcm images
        dicom_file = pydicom.dcmread(str(fn))
        arr = dicom_file.pixel_array.copy() 
        arr = arr * int(dicom_file.RescaleSlope) + int(dicom_file.RescaleIntercept) 
        level = 40; window = 80
        arr = np.clip(arr, level - window // 2, level + window // 2)
        x = Image.fromarray(arr).convert(convert_mode)
    if after_open: x = after_open(x)
    x = pil2tensor(x,np.float32)
    if div: x.div_(255)
    return cls(x)
3 Likes

Hi ! Fastai v2 include a full support for working with dicom images. Are these changed from 16 bits to 8 bits during common builtin transforms when loading?