What are Dicom images?

DICOM(Digital Imaging and COmmunications in Medicine) is the de-facto standard that establishes rules that allow medical images(X-Ray, MRI, CT) and associated information to be exchanged between imaging equipment from different vendors, computers, and hospitals. The DICOM format provides a suitable means that meets health information exchange (HIE) standards for transmission of health related data among facilites and HL7 standards which is the messaging standard that enables clinical applications to exchange data.

Workflow

DICOM files typically have a .dcm extension and provides a means of storing data in separate 'tags' such as patient information as well as image/pixel data. A DICOM file consists of a header and image data sets packed into a single file. The information within the header is organized as a constant and standardized series of tags. By extracting data from these tags one can access important information regarding the patient demographics, study parameters, etc

The header portion of a DICOM file most likely contains PHI(Protected Health Information) bearing in mind that pixel data may also contain PHI

16 bit DICOM images have values ranging from -32768 to 32768 while 8-bit greyscale images store values from 0 to 255. The value ranges in DICOM images are useful as they correlate with the Hounsfield Scale which is a quantitative scale for describing radio density

Parts of a DICOM

Dicom

Requirements

Requires installing pycidom

  • pip install pycidom

and scikit-image

  • pip install scikit-image

and kornia

  • pip install kornia

Fastai provides an easy to access slim dicom dataset (250 DICOM files, ~30MB) from the SIIM-ACR Pneumothorax Segmentation dataset for us to experiment with dicom images. The file structure of the dataset is as follows:

SIIM_SLIM

from fastai2.basics import *
from fastai2.callback.all import *
from fastai2.vision.all import *
from fastai2.medical.imaging import *

import pydicom
#Load the Data
pneumothorax_source = untar_data(URLs.SIIM_SMALL)

Patching

get_dicom_files

Provides a convenient way of recursively loading .dcm images from a folder. By default the folders option is set to False but you could specify a specific folder if required

#get dicom files
items = get_dicom_files(pneumothorax_source, recurse=True, folders='train')
items
(#250) [Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000000.dcm'),Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000002.dcm'),Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000005.dcm'),Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000006.dcm'),Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000007.dcm'),Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000008.dcm'),Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000009.dcm'),Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000011.dcm'),Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000012.dcm'),Path('C:/Users/avird/.fastai/data/siim_small/train/No Pneumothorax/000014.dcm')...]

dcmread

Pydicom is a python package for parsing DICOM files and makes it easy to covert DICOM files into pythonic structures for easier manipulation. Files are opened using pydicom.dcmread

img = items[10]
dimg = dcmread(img)
type(dimg)
pydicom.dataset.FileDataset

You can now view all the information of the DICOM file. Explanation of each element is beyond the scope of this tutorial but this site has some excellent information about each of the entries. Information is listed by the DICOM tag (eg: 0008, 0005) or DICOM keyword (eg: Specific Character Set)

dimg
(0008, 0005) Specific Character Set              CS: 'ISO_IR 100'
(0008, 0016) SOP Class UID                       UI: Secondary Capture Image Storage
(0008, 0018) SOP Instance UID                    UI: 1.2.276.0.7230010.3.1.4.8323329.6340.1517875197.696624
(0008, 0020) Study Date                          DA: '19010101'
(0008, 0030) Study Time                          TM: '000000.00'
(0008, 0050) Accession Number                    SH: ''
(0008, 0060) Modality                            CS: 'CR'
(0008, 0064) Conversion Type                     CS: 'WSD'
(0008, 0090) Referring Physician's Name          PN: ''
(0008, 103e) Series Description                  LO: 'view: AP'
(0010, 0010) Patient's Name                      PN: '13f40bdc-803d-4fe0-b008-21234c2be1c3'
(0010, 0020) Patient ID                          LO: '13f40bdc-803d-4fe0-b008-21234c2be1c3'
(0010, 0030) Patient's Birth Date                DA: ''
(0010, 0040) Patient's Sex                       CS: 'F'
(0010, 1010) Patient's Age                       AS: '74'
(0018, 0015) Body Part Examined                  CS: 'CHEST'
(0018, 5101) View Position                       CS: 'AP'
(0020, 000d) Study Instance UID                  UI: 1.2.276.0.7230010.3.1.2.8323329.6340.1517875197.696623
(0020, 000e) Series Instance UID                 UI: 1.2.276.0.7230010.3.1.3.8323329.6340.1517875197.696622
(0020, 0010) Study ID                            SH: ''
(0020, 0011) Series Number                       IS: "1"
(0020, 0013) Instance Number                     IS: "1"
(0020, 0020) Patient Orientation                 CS: ''
(0028, 0002) Samples per Pixel                   US: 1
(0028, 0004) Photometric Interpretation          CS: 'MONOCHROME2'
(0028, 0010) Rows                                US: 1024
(0028, 0011) Columns                             US: 1024
(0028, 0030) Pixel Spacing                       DS: [0.168, 0.168]
(0028, 0100) Bits Allocated                      US: 8
(0028, 0101) Bits Stored                         US: 8
(0028, 0102) High Bit                            US: 7
(0028, 0103) Pixel Representation                US: 0
(0028, 2110) Lossy Image Compression             CS: '01'
(0028, 2114) Lossy Image Compression Method      CS: 'ISO_10918_1'
(7fe0, 0010) Pixel Data                          OB: Array of 118256 elements

Some key pointers on the tag information above:

  • Pixel Data (7fe0 0010) - This is where the raw pixel data is stored. The order of pixels encoded for each image plane is left to right, top to bottom, i.e., the upper left pixel (labeled 1,1) is encoded first
  • Photometric Interpretation (0028, 0004) - aka color space. In this case it is MONOCHROME2 where pixel data is represented as a single monochrome image plane where the minimum sample value is intended to be displayed as black info
  • Samples per Pixel (0028, 0002) - This should be 1 as this image is monochrome. This value would be 3 if the color space was RGB for example
  • Bits Stored (0028 0101) - Number of bits stored for each pixel sample
  • Pixel Represenation (0028 0103) - can either be unsigned(0) or signed(1). The default is unsigned. This Kaggle notebook by Jeremy explains why BitsStored and PixelRepresentation are important
  • Lossy Image Compression (0028 2110) - 00 image has not been subjected to lossy compression. 01 image has been subjected to lossy compression.
  • Lossy Image Compression Method (0028 2114) - states the type of lossy compression used (in this case JPEG Lossy Compression)

Important tags not included in this dataset:

  • Rescale Intercept (0028, 1052) - The value b in relationship between stored values (SV) and the output units. Output units = m*SV + b.
  • Rescale Slope (0028, 1053) - m in the equation specified by Rescale Intercept (0028,1052).

The Rescale Intercept and Rescale Slope are applied to transform the pixel values of the image into values that are meaningful to the application. Calculating the new values usually follow a linear formula:

  • NewValue = (RawPixelValue * RescaleSlope) + RescaleIntercept

and when the relationship is not linear a LUT(LookUp Table) is utilized.

By default pydicom reads pixel data as the raw bytes found in the file and typically PixelData is often not immediately useful as data may be stored in a variety of different ways:

  • The pixel values may be signed or unsigned integers, or floats
  • There may be multiple image frames
  • There may be multiple planes per frame (i.e. RGB) and the order of the pixels may be different These are only a few examples and more information can be found on the pycidom website
dimg.PixelData[:200]
b'\xfe\xff\x00\xe0\x00\x00\x00\x00\xfe\xff\x00\xe0\xe0\xcd\x01\x00\xff\xd8\xff\xdb\x00C\x00\x03\x02\x02\x02\x02\x02\x03\x02\x02\x02\x03\x03\x03\x03\x04\x06\x04\x04\x04\x04\x04\x08\x06\x06\x05\x06\t\x08\n\n\t\x08\t\t\n\x0c\x0f\x0c\n\x0b\x0e\x0b\t\t\r\x11\r\x0e\x0f\x10\x10\x11\x10\n\x0c\x12\x13\x12\x10\x13\x0f\x10\x10\x10\xff\xc0\x00\x0b\x08\x04\x00\x04\x00\x01\x01\x11\x00\xff\xc4\x00\x1d\x00\x00\x02\x02\x03\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x04\x02\x05\x00\x01\x06\x07\x08\t\xff\xc4\x00]\x10\x00\x01\x04\x01\x03\x02\x05\x01\x04\x05\x05\n\x08\x0b\x05\t\x01\x00\x02\x03\x11\x04\x12!1\x05A\x06\x13"Qaq\x142\x81\x91\x07#B\xa1\xb1\x15R\xc1\xd1\xd2\x08\x16$3b\x92\x95\xb2\xb3\xe1%CSr\x82\x93\xa2'

Because of the complexity in interpreting PixelData, pydicom provides an easy way to get it in a convenient form: pixel_array which returns a numpy.ndarray containing the pixel data:

dimg.pixel_array, dimg.pixel_array.shape
(array([[  2,   6,   5, ...,   3,   3,   2],
        [  5,   9,   8, ...,   6,   5,   5],
        [  5,   9,   9, ...,   6,   5,   5],
        ...,
        [ 49,  85,  80, ..., 123, 121,  69],
        [ 54,  88,  81, ..., 118, 115,  70],
        [ 17,  48,  39, ...,  46,  52,  27]], dtype=uint8), (1024, 1024))

Class TensorDicom

Inherits from TensorImage

Class PILDicom

Inherits from PILBase

Opens a DICOM file from path fn or bytes fn and load it as a PIL Image. The DICOM is opened using pydicom.dcmread and accesses the pixel_array

type(PILDicom.create(img))
fastai2.medical.imaging.PILDicom

pixels

Converts a pixel_array to a tensor

pixels(dimg)
tensor([[  2.,   6.,   5.,  ...,   3.,   3.,   2.],
        [  5.,   9.,   8.,  ...,   6.,   5.,   5.],
        [  5.,   9.,   9.,  ...,   6.,   5.,   5.],
        ...,
        [ 49.,  85.,  80.,  ..., 123., 121.,  69.],
        [ 54.,  88.,  81.,  ..., 118., 115.,  70.],
        [ 17.,  48.,  39.,  ...,  46.,  52.,  27.]])

scaled_pixel

pixels scaled by RescaleSlope and RescaleIntercept. The slim SIIM_SMALL dataset does not have RescaleSlope (0028,1053) or RescaleIntercept (0028,1052) in the dataset.

>> Side Note: Pixel Distribution

Having well scaled inputs is really important in getting good results from neural net training ref. This means having a normal or uniform distribution. The pixels in the DICOM image do not show a uniform distribution

px = dimg.pixels.flatten()
plt.hist(px, bins=50, color='c');

In this case the image is showing multimodal distribution(having more than 2 peaks). Another case could be where the distribution is bimodal(having 2 distinct peaks). The functions below provide a means of splitting the range of pixel values into groups so that each group has an equal number of pixels

array_freqhist_bins

#Todo

Tensor.freqhist_bins

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

Convert pydicom.dataset.FileDataset into a tensor

tensor_dicom = pixels(dimg)
tensor_dicom
tensor([[  2.,   6.,   5.,  ...,   3.,   3.,   2.],
        [  5.,   9.,   8.,  ...,   6.,   5.,   5.],
        [  5.,   9.,   9.,  ...,   6.,   5.,   5.],
        ...,
        [ 49.,  85.,  80.,  ..., 123., 121.,  69.],
        [ 54.,  88.,  81.,  ..., 118., 115.,  70.],
        [ 17.,  48.,  39.,  ...,  46.,  52.,  27.]])
t_bin = tensor_dicom.freqhist_bins(n_bins=100)
t_bin, t_bin.shape
(tensor([  3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,  11.,  13.,  16.,  21.,
          26.,  31.,  38.,  45.,  52.,  57.,  62.,  65.,  68.,  70.,  72.,  74.,
          76.,  78.,  79.,  81.,  82.,  83.,  84.,  86.,  87.,  88.,  89.,  90.,
          91.,  92.,  93.,  94.,  95.,  96.,  97.,  98.,  99., 100., 101., 102.,
         103., 104., 105., 106., 107., 108., 109., 110., 111., 112., 113., 115.,
         116., 117., 119., 120., 122., 123., 124., 126., 127., 129., 131., 132.,
         134., 136., 138., 140., 143., 145., 148., 151., 155., 158., 161., 163.,
         165., 167., 169., 171., 173., 175., 177., 179., 180., 182., 184., 186.,
         189., 192., 197., 203.]), torch.Size([100]))

freqhist_bins splits the pixels into bins, the number of bins set by n_bins. So for the above example:

  • freqhist_bins flattens out the image tensor (in this case 1024 by 1024 into a flattened tensor of size 1048576 (1024*1024)
  • setting n_bins to 1 for example means it will be split into 3 bins (the beginning, the end and the number of bins specified by n_bins
  • each bin is then scaled to values between 0 and 255 (in this case the bulk of pixels are grouped at 3, 103 and 203

with a n_bin of 1

plt.hist(t_bin, bins=t_bin, color='c'); plt.plot(t_bin, torch.linspace(0,1,len(t_bin)));dimg.show(t_bin)

with n_bins of 100 the histogram displays that each group has the same number of pixels and you can also notice that the quality of the image is different compared to the image above

plt.hist(t_bin, bins=t_bin, color='c'); plt.plot(t_bin, torch.linspace(0,1,len(t_bin)));dimg.show(t_bin)

Tensor.hist_scaled_pt

A way of scaling tensors using CUDA

Tensor.hist_scaled

A way to scale a tensor of pixels evenly using freqhist_bins to values between 0 and 1.

tensor_dicom
tensor([[  2.,   6.,   5.,  ...,   3.,   3.,   2.],
        [  5.,   9.,   8.,  ...,   6.,   5.,   5.],
        [  5.,   9.,   9.,  ...,   6.,   5.,   5.],
        ...,
        [ 49.,  85.,  80.,  ..., 123., 121.,  69.],
        [ 54.,  88.,  81.,  ..., 118., 115.,  70.],
        [ 17.,  48.,  39.,  ...,  46.,  52.,  27.]])

The tensor has values between 0 and 255

#Run to view - commented out to reduce file size
#plt.hist(tensor_dicom, bins=100)

The above commented out code displays a histogram of pixel values which range from 0 to 255

Histogram

Using hist_scaled gets values now scaled between 0 and 1

tensor_hists = tensor_dicom.hist_scaled()
tensor_hists
tensor([[0.0000, 0.0303, 0.0202,  ..., 0.0000, 0.0000, 0.0000],
        [0.0202, 0.0606, 0.0505,  ..., 0.0303, 0.0202, 0.0202],
        [0.0202, 0.0606, 0.0606,  ..., 0.0303, 0.0202, 0.0202],
        ...,
        [0.1573, 0.3081, 0.2677,  ..., 0.6566, 0.6414, 0.2071],
        [0.1657, 0.3333, 0.2727,  ..., 0.6212, 0.5960, 0.2121],
        [0.1030, 0.1558, 0.1429,  ..., 0.1530, 0.1616, 0.1232]])
#plotting the scaled histogram
#plt.hist(tensor_hists, bins=100)

Scaled histogram now has pixel values ranging from 0 to 1

hist2

Dataset.hist_scaled

dimg.pixel_array
array([[  2,   6,   5, ...,   3,   3,   2],
       [  5,   9,   8, ...,   6,   5,   5],
       [  5,   9,   9, ...,   6,   5,   5],
       ...,
       [ 49,  85,  80, ..., 123, 121,  69],
       [ 54,  88,  81, ..., 118, 115,  70],
       [ 17,  48,  39, ...,  46,  52,  27]], dtype=uint8)
data_scaled = dimg.hist_scaled(min_px=1, max_px=10)
data_scaled
tensor([[0.0000, 0.4286, 0.2857,  ..., 0.0000, 0.0000, 0.0000],
        [0.2857, 0.8571, 0.7143,  ..., 0.4286, 0.2857, 0.2857],
        [0.2857, 0.8571, 0.8571,  ..., 0.4286, 0.2857, 0.2857],
        ...,
        [1.0000, 1.0000, 1.0000,  ..., 1.0000, 1.0000, 1.0000],
        [1.0000, 1.0000, 1.0000,  ..., 1.0000, 1.0000, 1.0000],
        [1.0000, 1.0000, 1.0000,  ..., 1.0000, 1.0000, 1.0000]])
#max_px = 10
plt.imshow(data_scaled, cmap=plt.cm.bone);
#max_px = 100
data_scaled2 = dimg.hist_scaled(min_px=1, max_px=100)
plt.imshow(data_scaled2, cmap=plt.cm.bone)
<matplotlib.image.AxesImage at 0x2472852ec48>

>> Side Note: Windowing

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

Example: from here

Brain Matter window

l = 40 (window center) w = 80 (window width)

Voxels displayed range from 0 to 80

Calculating voxel values:

  • lowest_visible_value = window_center - window_width / 2
  • highest_visible_value = window_center + window_width / 2

(lowest_visible_value = 40 - (80/2), highest_visible_value = 40 + (80/2))

Hence all values above >80 will be white and all values below 0 are black.

Dataset.windowed

Takes 2 values w and l

fastai conveniently provides a range of window width and centers (dicom_windows) for viewing common body areas:

brain=(80,40),
subdural=(254,100),
stroke=(8,32),
brain_bone=(2800,600),
brain_soft=(375,40),
lungs=(1500,-600),
mediastinum=(350,50),
abdomen_soft=(400,50),
liver=(150,30),
spine_soft=(250,50),
spine_bone=(1800,400)
plt.imshow(dimg.windowed(w=1500, l=-600), cmap=plt.cm.bone)
<matplotlib.image.AxesImage at 0x247280734c8>
plt.imshow(dimg.windowed(*dicom_windows.lungs), cmap=plt.cm.bone)
<matplotlib.image.AxesImage at 0x24727ae4ec8>

Example

img = items[10]
dimg = dcmread(img)

Convert a DICOM image into tensors

tensor_dicom = pixels(dimg)
tensor_dicom
tensor([[  2.,   6.,   5.,  ...,   3.,   3.,   2.],
        [  5.,   9.,   8.,  ...,   6.,   5.,   5.],
        [  5.,   9.,   9.,  ...,   6.,   5.,   5.],
        ...,
        [ 49.,  85.,  80.,  ..., 123., 121.,  69.],
        [ 54.,  88.,  81.,  ..., 118., 115.,  70.],
        [ 17.,  48.,  39.,  ...,  46.,  52.,  27.]])

View a portion of the image

e = tensor(tensor_dicom)[200:440,600:840]
e.shape
torch.Size([240, 240])
df = pd.DataFrame(e)

#commented out conserve memory
#df.style.set_properties(**{'font-size':'6pt'}).background_gradient('Greys')

The dataframe created by df looks like this:

Dataframe

dicom_windows = types.SimpleNamespace(
    test=(50,60)
)
r = e.windowed(w=50, l=60)
#This will produce a Dataframe of each pixel whether 'true' or 'false'
#commented out due to high memory use

test_e = e.mask_from_blur(dicom_windows.test, thresh=0.05, remove_max=True)
#dfe = pd.DataFrame(test_e)
#dfe.style.set_properties(**{'font-size':'6pt'}).background_gradient('Greys')

Dataframe2

imk = [e,r, test_e]
show_images(imk, titles=('orig', 'windowed', 'mask'), figsize=(15,15))

using show_images the images appear green because show_images and show_image by default use the 'viridis' colormap and PILBase follows the same default colormap. Its important to note that the colormap choice is for the human benefit (it does not actually affect model training). However looking at the image above it looks very un-natural and hence better to change to another colormap choice. The 'bone' colormap displays images with bone colors (which is what show uses)

matplotlib.rcParams['image.cmap'] = 'bone'
imk = [e,r, test_e]
show_images(imk, titles=('orig', 'windowed', 'mask'), figsize=(15,15))

show

Inherits from show_image and is customized for displaying DICOM images

dimg.show(scale=True, cmap=plt.cm.bone, min_px=-1100, max_px=None, ax=None, figsize=(6,6), title='Test', ctx=None)

Jeremy mentioned in this article about using a 'rainbow colormap' to fully utilize our computer's ability to display color

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

Class TensorCTScan

Inherits from PILBase

Class PILCTScan

Inherits from PILBase

Dataset.pct_in_window

Gets the % of pixels within a window w, l

For example using lungs as the dicom_window (1500,-600) we see that 80% of the pixels are within the window

dimg.pct_in_window(*dicom_windows.lungs)
0.7996940612792969

uniform_blur2d

#Todo
ims = dimg.hist_scaled(), uniform_blur2d(dimg.hist_scaled(),50)
show_images(ims, titles=('orig', 'blurred'), figsize=(10,10))

gauss_blur2d

#Todo

Uses gaussian_blur2d kornia filter

gims = dimg.hist_scaled(), gauss_blur2d(dimg.hist_scaled(),50)
show_images(gims, titles=('orig', 'gauss_blurred'), figsize=(10,10))

As explained in this notebook Jeremy uses these methods in cleaning the data in order to only retain information that is relevant. You use gauss_blur2d to blur the image and select the areas that are bright

px = dimg.windowed(*dicom_windows.lungs)
show_image(px, figsize=(4,4));
blurred = gauss_blur2d(px, 100)
show_image(blurred, figsize=(4,4));
show_image(blurred>1);

Dataset.mask_from_blur

test = dimg.hist_scaled()
blur = uniform_blur2d(dimg.hist_scaled(),50)
ims = [test, blur]
show_images(ims, titles=('orig', 'blurred'), figsize=(10,10))
dicom_windows.brain
(80, 40)
testblur = dimg.hist_scaled(min_px=0, max_px=100)
blur = uniform_blur2d(dimg.hist_scaled(),50)
test_mask = dimg.mask_from_blur(dicom_windows.brain, sigma=0.3, thresh=0.01, remove_max=True)
ims = [testblur,test_mask]
show_images(ims, titles=('orig', 'mask'), figsize=(10,10))

All values above +80 will be white and all values below 0 are black.

mask2bbox

#Todo
bbs = mask2bbox(test_mask)
lo,hi = bbs
print(bbs)
print(lo[0], hi)
tensor([[   0,    0],
        [1023, 1023]], device='cuda:0')
tensor(0, device='cuda:0') tensor([1023, 1023], device='cuda:0')

crop_resize

#Todo
def _bbs2sizes(crops, init_sz, use_square=True):
    bb = crops.flip(1)
    print(f'bb" {bb} shape: {bb.shape}')
    szs = (bb[1]-bb[0])
    print(f'first szs: {szs} shape: {szs.shape}')
    if use_square: szs = szs.max(0)[0][None].repeat((2,1))
    overs = (szs+bb[0])>init_sz
    print(f'overs: {overs}')
    bb[0][overs] = init_sz-szs[overs]
    lows = (bb[0]/float(init_sz))
    return lows,szs/float(init_sz)
def crop_resize(x, crops, new_sz):
    # NB assumes square inputs. Not tested for non-square anythings!
    bs = x.shape[0] #number of channels
    print(bs)
    lows,szs = _bbs2sizes(crops, x.shape[-1])
    print(f'lows: {lows}\n szs:{szs}')
    if not isinstance(new_sz,(list,tuple)): new_sz = (new_sz,new_sz)
    id_mat = tensor([[1.,0,0],[0,1,0]])[None].repeat((bs,1,1)).to(x.device)
    with warnings.catch_warnings():
        warnings.filterwarnings('ignore', category=UserWarning)
        sp = F.affine_grid(id_mat, (bs,1,*new_sz))+1.
        grid = sp*unsqueeze(szs.t(),1,n=2)+unsqueeze(lows.t()*2.,1,n=2)
        return F.grid_sample(x.unsqueeze(1), grid-1)

Dataset.to_nchan

#Todo
show_images(dimg.to_nchan([dicom_windows.brain,dicom_windows.subdural,dicom_windows.abdomen_soft]))
show_images(dimg.to_nchan([dicom_windows.brain]))

Dataset.to_3chan

#Todo

Tensor.save_jpg

Save a tensor into a .jpg with specified windows

tensor_dicom = pixels(dimg)
tensor_dicom.save_jpg(path=(pneumothorax_source/f'train/01tensor.jpg'), wins=[dicom_windows.lungs, dicom_windows.subdural])
show_image(Image.open(pneumothorax_source/f'train/01tensor.jpg'))
<matplotlib.axes._subplots.AxesSubplot at 0x239905644c8>

Dataset.save_jpg

Save a DICOM image into a .jpg

dimg.save_jpg(path=(pneumothorax_source/f'train/01dicom.jpg'), wins=[dicom_windows.lungs, dicom_windows.lungs])
show_image(Image.open(pneumothorax_source/f'train/01dicom.jpg'))
<matplotlib.axes._subplots.AxesSubplot at 0x2398cd85f08>

Dataset.set_pixels

#Todo

Dataset.zoom

Zoom's image by specifying pixel ratio

dimg2 = dcmread(items[11])
dimg2.zoom(0.1)
dimg2.show()
dimg2.zoom(0.5)
dimg2.show()

Dataset.zoom_to

dimg2 = dcmread(items[11])
dimg2.shape, dimg2.show()
((1024, 1024), None)
dimg2.zoom_to(90)
dimg2.shape, dimg2.show()
((90, 90), None)