Medical Imaging

Open In Colab

Helpers for working with DICOM files

  1. /usr/local/lib/python3.8/dist-packages/torch/cuda/__init__.py:52: UserWarning: CUDA initialization: Found no NVIDIA driver on your system. Please check that you have an NVIDIA GPU and installed a driver from http://www.nvidia.com/Download/index.aspx (Triggered internally at /pytorch/c10/cuda/CUDAFunctions.cpp:100.)
  2. return torch._C._cuda_getDeviceCount() > 0

Patching

get_dicom_files[source]

get_dicom_files(path, recurse=True, folders=None)

Get dicom files in path recursively, only in folders, if specified.

Path.dcmread[source]

Path.dcmread(fn:Path, force=False)

Open a DICOM file

fastai.medical.imaging uses pydicom.dcmread to read a DICOM file. To view the header of a DICOM, specify the path of a test file and call dcmread.

  1. TEST_DCM = Path('images/sample.dcm')
  2. dcm = TEST_DCM.dcmread()
  3. dcm
  1. Dataset.file_meta -------------------------------
  2. (0002, 0000) File Meta Information Group Length UL: 176
  3. (0002, 0001) File Meta Information Version OB: b'x00x01'
  4. (0002, 0002) Media Storage SOP Class UID UI: CT Image Storage
  5. (0002, 0003) Media Storage SOP Instance UID UI: 9999.180975792154576730321054399332994563536
  6. (0002, 0010) Transfer Syntax UID UI: Explicit VR Little Endian
  7. (0002, 0012) Implementation Class UID UI: 1.2.40.0.13.1.1.1
  8. (0002, 0013) Implementation Version Name SH: 'dcm4che-1.4.38'
  9. -------------------------------------------------
  10. (0008, 0018) SOP Instance UID UI: ID_e0cc6a4b5
  11. (0008, 0060) Modality CS: 'CT'
  12. (0010, 0020) Patient ID LO: 'ID_a107dd7f'
  13. (0020, 000d) Study Instance UID UI: ID_6468bdd34a
  14. (0020, 000e) Series Instance UID UI: ID_4be303ae64
  15. (0020, 0010) Study ID SH: ''
  16. (0020, 0032) Image Position (Patient) DS: [-125.000, -122.268, 115.936]
  17. (0020, 0037) Image Orientation (Patient) DS: [1.000000, 0.000000, 0.000000, 0.000000, 0.978148, -0.207912]
  18. (0028, 0002) Samples per Pixel US: 1
  19. (0028, 0004) Photometric Interpretation CS: 'MONOCHROME2'
  20. (0028, 0010) Rows US: 256
  21. (0028, 0011) Columns US: 256
  22. (0028, 0030) Pixel Spacing DS: [0.488281, 0.488281]
  23. (0028, 0100) Bits Allocated US: 16
  24. (0028, 0101) Bits Stored US: 16
  25. (0028, 0102) High Bit US: 15
  26. (0028, 0103) Pixel Representation US: 1
  27. (0028, 1050) Window Center DS: "40.0"
  28. (0028, 1051) Window Width DS: "100.0"
  29. (0028, 1052) Rescale Intercept DS: "-1024.0"
  30. (0028, 1053) Rescale Slope DS: "1.0"
  31. (7fe0, 0010) Pixel Data OW: Array of 131072 elements
  1. type(dcm)
  1. pydicom.dataset.FileDataset

class TensorDicom[source]

TensorDicom(x, **kwargs) :: TensorImage

Inherits from TensorImage and converts the pixel_array into a TensorDicom

class PILDicom[source]

PILDicom() :: PILBase

This class represents an image object. To create :py:class:~PIL.Image.Image objects, use the appropriate factory functions. There’s hardly ever any reason to call the Image constructor directly.

  • :py:func:~PIL.Image.open
  • :py:func:~PIL.Image.new
  • :py:func:~PIL.Image.frombytes

Path.png16read[source]

Path.png16read()

None[source]

  1. dcm.pixels
  1. tensor([[-1024., -1024., -1024., ..., -1024., -1024., -1024.],
  2. [-1024., -1024., -1024., ..., -1024., -1024., -1024.],
  3. [-1024., -1024., -1024., ..., -1024., -1024., -1024.],
  4. ...,
  5. [-1024., -1024., -1024., ..., -1024., -1024., -1024.],
  6. [-1024., -1024., -1024., ..., -1024., -1024., -1024.],
  7. [-1024., -1024., -1024., ..., -1024., -1024., -1024.]])

None[source]

scaled_px uses RescaleSlope and RescaleIntercept values to correctly scale the image so that they represent the correct tissue densities. You can observe what scaled_px does by viewing the the pixel distribution of a dicom image. The histogram below displays the current pixel distribution which shows a pixel range between -1133 and 2545.

  1. plt.hist(dcm.pixels.flatten().numpy());

Imagery - 图2

As shown in the header of the test image the RescaleIntercept has a value of -1024.0 and a RescaleSlope value of 1.0. scaled_px will scale the pixels by these values.

  1. plt.hist(dcm.scaled_px.flatten().numpy());

Imagery - 图3

The pixel distibution is now between -2157 and 1521

array_freqhist_bins[source]

array_freqhist_bins(n_bins=100)

A numpy based function to split the range of pixel values into groups, such that each group has around the same number of pixels

Tensor.freqhist_bins[source]

Tensor.freqhist_bins(n_bins=100)

A function to split the range of pixel values into groups, such that each group has around the same number of pixels

For example with n_bins set to 1 this means the bins will be split into 3 distinct bins (the beginning, the end and the number of bins specified by n_bins.

  1. t_bin = dcm.pixels.freqhist_bins(n_bins=1)
  2. t_bin
  1. tensor([-1076., 40., 2375.])
  1. plt.hist(t_bin.numpy(), bins=t_bin, color='c')
  2. plt.plot(t_bin, torch.linspace(0,1,len(t_bin)));

Imagery - 图4

with n_bins at 100

  1. t_bin = dcm.pixels.freqhist_bins(n_bins=100)
  2. t_bin
  1. tensor([-1076., -1026., -1024., -1021., 28., 30., 31., 32., 33.,
  2. 34., 35., 36., 37., 38., 39., 40., 41., 42.,
  3. 44., 48., 52., 58., 66., 72., 76., 80., 85.,
  4. 91., 94., 98., 103., 111., 123., 161., 219., 478.,
  5. 829., 999., 1027., 1038., 1044., 1047., 1049., 1050., 1051.,
  6. 1052., 1053., 1054., 1055., 1056., 1057., 1058., 1059., 1060.,
  7. 1062., 1066., 1108., 1265., 1453., 1616., 1741., 1838., 1943.,
  8. 2051., 2220., 2375.])
  1. plt.hist(t_bin.numpy(), bins=t_bin, color='c'); plt.plot(t_bin, torch.linspace(0,1,len(t_bin)));

Imagery - 图5

Tensor.hist_scaled_pt[source]

Tensor.hist_scaled_pt(brks=None)

Tensor.hist_scaled[source]

Tensor.hist_scaled(brks=None)

Scales a tensor using freqhist_bins to values between 0 and 1

The test image has pixel values that range between -1000 and 2500

  1. plt.hist(dcm.pixels.flatten().numpy(), bins=100);

Imagery - 图6

hist_scaled provides a way of scaling the input pixel values to between 0 and 1

  1. tensor_hists = dcm.pixels.hist_scaled()
  2. plt.hist(tensor_hists.flatten().numpy(), bins=100);

Imagery - 图7

Dataset.hist_scaled[source]

Dataset.hist_scaled(brks=None, min_px=None, max_px=None)

Pixels scaled to a min_px and max_px value

  1. data_scaled = dcm.hist_scaled()
  2. plt.imshow(data_scaled, cmap=plt.cm.bone);

Imagery - 图8

  1. data_scaled = dcm.hist_scaled(min_px=100, max_px=1000)
  2. plt.imshow(data_scaled, cmap=plt.cm.bone);

Imagery - 图9

Dicom images can contain a high amount of voxel values and windowing can be thought of as a means of manipulating these values in order to change the apperance of the image so particular structures are highlighted. A window has 2 values:

  • l = window level or center aka brightness
  • w = window width or range aka contrast

Tensor.windowed[source]

Tensor.windowed(w, l)

Scale pixel intensity by window width and window level

Dataset.windowed[source]

Dataset.windowed(w, l)

  1. plt.imshow(dcm.windowed(*dicom_windows.brain), cmap=plt.cm.bone);

Imagery - 图10

class TensorCTScan[source]

TensorCTScan(x, **kwargs) :: TensorImageBW

Inherits from TensorImageBW and converts the pixel_array into a TensorCTScan

  1. tensor_ct = TensorCTScan(dcm.pixel_array)
  2. tensor_ct.show();

Imagery - 图11

class PILCTScan[source]

PILCTScan() :: PILBase

This class represents an image object. To create :py:class:~PIL.Image.Image objects, use the appropriate factory functions. There’s hardly ever any reason to call the Image constructor directly.

  • :py:func:~PIL.Image.open
  • :py:func:~PIL.Image.new
  • :py:func:~PIL.Image.frombytes

Dataset.show[source]

Dataset.show(frames=1, scale=True, cmap=<matplotlib.colors.LinearSegmentedColormap object at 0x7fc7bbbefb80>, min_px=-1100, max_px=None, **kwargs)

Adds functionality to view dicom images where each file may have more than 1 frame

  1. scales = False, True, dicom_windows.brain, dicom_windows.subdural
  2. titles = 'raw','normalized','brain windowed','subdural windowed'
  3. for s,a,t in zip(scales, subplots(2,2,imsize=4)[1].flat, titles):
  4. dcm.show(scale=s, ax=a, title=t)

Imagery - 图12

  1. dcm.show(cmap=plt.cm.gist_ncar, figsize=(6,6))

Imagery - 图13

Some dicom datasets such as the The Thyroid Segmentation in Ultrasonography Dataset is a dataset where each image has multiple frames per file (hundreds in this case). By default the show function will display 1 frame but if the dataset has multiple frames you can specify the number of frames to view.

Dataset.show[source]

Dataset.show(frames=1, scale=True, cmap=<matplotlib.colors.LinearSegmentedColormap object at 0x7fc7bbbefb80>, min_px=-1100, max_px=None, **kwargs)

Adds functionality to view dicom images where each file may have more than 1 frame

  1. dcm.show()

Imagery - 图14

Dataset.pct_in_window[source]

Dataset.pct_in_window(dcm:Dataset, w, l)

% of pixels in the window (w,l)

  1. dcm.pct_in_window(*dicom_windows.brain)
  1. 0.19049072265625

pct_in_window can be used to check what percentage of the image is composed of meaningful pixels (pixels within the specified window)

uniform_blur2d[source]

uniform_blur2d(x, s)

Uniformly apply blurring

  1. ims = dcm.hist_scaled(), uniform_blur2d(dcm.hist_scaled(), 20), uniform_blur2d(dcm.hist_scaled(), 50)
  2. show_images(ims, titles=('original', 'blurred 20', 'blurred 50'))

Imagery - 图15

gauss_blur2d[source]

gauss_blur2d(x, s)

Apply gaussian_blur2d kornia filter

  1. ims = dcm.hist_scaled(), gauss_blur2d(dcm.hist_scaled(), 20), gauss_blur2d(dcm.hist_scaled(), 50)
  2. show_images(ims, titles=('original', 'gauss_blur 20', 'gauss_blur 50'))

Imagery - 图16

Images are often affected by random variations in intensity values, called noise. Gaussian noise contains variatons in intensity that are drawn from a Gaussian or normal distribution. A Guassian filter is usually used to blur edges and remove smaller or thinner areas in order to preserve the most important information

Tensor.mask_from_blur[source]

Tensor.mask_from_blur(x:Tensor, window, sigma=0.3, thresh=0.05, remove_max=True)

Create a mask from the blurred image

Dataset.mask_from_blur[source]

Dataset.mask_from_blur(x:Dataset, window, sigma=0.3, thresh=0.05, remove_max=True)

Create a mask from the blurred image

  1. mask = dcm.mask_from_blur(dicom_windows.brain, sigma=0.9, thresh=0.1, remove_max=True)
  2. wind = dcm.windowed(*dicom_windows.brain)
  3. _,ax = subplots(1,3)
  4. show_image(wind, ax=ax[0], title='window')
  5. show_image(mask, alpha=0.5, cmap=plt.cm.Reds, ax=ax[1], title='mask')
  6. show_image(wind, ax=ax[2])
  7. show_image(mask, alpha=0.5, cmap=plt.cm.Reds, ax=ax[2], title='window and mask');

Imagery - 图17

mask2bbox[source]

mask2bbox(mask)

  1. bbs = mask2bbox(mask)
  2. lo,hi = bbs
  3. show_image(wind[lo[0]:hi[0],lo[1]:hi[1]]);

Imagery - 图18

crop_resize[source]

crop_resize(x, crops, new_sz)

  1. px256 = crop_resize(to_device(wind[None]), bbs[...,None], 128)[0]
  2. show_image(px256)
  3. px256.shape
  1. torch.Size([1, 128, 128])

Imagery - 图19

Comparing the original image with the image from using the mask and crop_resize function

  1. _,axs = subplots(1,2)
  2. dcm.show(ax=axs[0])
  3. show_image(px256, ax=axs[1]);

Imagery - 图20

Tensor.to_nchan[source]

Tensor.to_nchan(x:Tensor, wins, bins=None)

Dataset.to_nchan[source]

Dataset.to_nchan(x:Dataset, wins, bins=None)

to_nchan takes a tensor or a dicom as the input and returns multiple one channel images (the first depending on the choosen windows and a normalized image). Setting bins to 0 only returns the windowed image.

  1. show_images(dcm.to_nchan([dicom_windows.brain], bins=0))

Imagery - 图21

  1. show_images(dcm.to_nchan([dicom_windows.brain], bins=None))

Imagery - 图22

Tensor.to_3chan[source]

Tensor.to_3chan(x:Tensor, win1, win2, bins=None)

Dataset.to_3chan[source]

Dataset.to_3chan(x:Dataset, win1, win2, bins=None)

  1. show_images(dcm.to_nchan([dicom_windows.brain,dicom_windows.subdural,dicom_windows.abdomen_soft]))

Imagery - 图23

Tensor.save_jpg[source]

Tensor.save_jpg(x:Dataset'>), path, wins, bins=None, quality=90)

Save tensor or dicom image into jpg format

Dataset.save_jpg[source]

Dataset.save_jpg(x:Dataset'>), path, wins, bins=None, quality=90)

Save tensor or dicom image into jpg format

Tensor.to_uint16[source]

Tensor.to_uint16(x:Dataset'>), bins=None)

Convert into a unit16 array

Dataset.to_uint16[source]

Dataset.to_uint16(x:Dataset'>), bins=None)

Convert into a unit16 array

Tensor.save_tif16[source]

Tensor.save_tif16(x:Dataset'>), path, bins=None, compress=True)

Save tensor or dicom image into tiff format

Dataset.save_tif16[source]

Dataset.save_tif16(x:Dataset'>), path, bins=None, compress=True)

Save tensor or dicom image into tiff format

  1. _,axs=subplots(1,2)
  2. with tempfile.TemporaryDirectory() as f:
  3. f = Path(f)
  4. dcm.save_jpg(f/'test.jpg', [dicom_windows.brain,dicom_windows.subdural])
  5. show_image(Image.open(f/'test.jpg'), ax=axs[0])
  6. dcm.save_tif16(f/'test.tif')
  7. show_image(Image.open(str(f/'test.tif')), ax=axs[1]);

Imagery - 图24

Dataset.set_pixels[source]

Dataset.set_pixels(px)

Dataset.zoom[source]

Dataset.zoom(ratio)

Zoom image by specified ratio

Check to see the current size of the dicom image

  1. dcm.pixel_array.shape
  1. (256, 256)
  1. dcm.zoom(7.0)
  2. dcm.show(); dcm.pixel_array.shape
  1. (1792, 1792)

Imagery - 图25

Dataset.zoom_to[source]

Dataset.zoom_to(sz)

Change image size to specified pixel size

  1. dcm.zoom_to(200); dcm.pixel_array.shape
  1. (200, 200)

None[source]

  1. dcm2 = TEST_DCM.dcmread()
  2. dcm2.zoom_to(90)
  3. test_eq(dcm2.shape, (90,90))
  1. dcm2 = TEST_DCM.dcmread()
  2. dcm2.zoom(0.25)
  3. dcm2.show()

Imagery - 图26

Dataset.as_dict[source]

Dataset.as_dict(px_summ=True, window=(80, 40))

Convert the header of a dicom into a dictionary

as_dict takes in 2 parameters: px_summ which by default is set to True and this returns additional stats such as minimal pixel value, maximum pixel value, the mean pixel value and the image standard deviation. The window parameter calculates the pct_in_window value depending on the window that is specified.

  1. dcm.as_dict(px_summ=True, window=dicom_windows.brain);

Creating a dataframe of the values within the header of the dicom

  1. pneumothorax_source = untar_data(URLs.SIIM_SMALL)
  2. items = get_dicom_files(pneumothorax_source, recurse=True, folders='train')
  3. dicom_dataframe = pd.DataFrame.from_dicoms(items, window=dicom_windows.brain)
  4. dicom_dataframe.head(2).T.tail(5)
01
img_min00
img_max254250
img_mean160.398114.525
img_std53.854970.7523
img_pct_window0.08702950.326269

class DicomSegmentationDataLoaders[source]

DicomSegmentationDataLoaders(*loaders, path='.', device=None) :: DataLoaders

Basic wrapper around DICOM DataLoaders with factory methods for segmentation problems

  1. path = untar_data(URLs.TCGA_SMALL)
  2. codes = np.loadtxt(path/'codes.txt', dtype=str)
  3. fnames = get_dicom_files(path/'dicoms')
  4. label_func = lambda o: path/'labels'/f'{o.stem}.png'
  5. dls = DicomSegmentationDataLoaders.from_label_func(path, fnames, label_func, codes=codes, bs=4)
  6. dls.show_batch()

Imagery - 图27


Company logo

©2021 fast.ai. All rights reserved.
Site last generated: Mar 31, 2021