# pydicom_PIL.py """View DICOM images using Python image Library (PIL) Usage: >>> import pydicom >>> from pydicom.contrib.pydicom_PIL import show_PIL >>> ds = pydicom.read_file("filename") >>> show_PIL(ds) Requires Numpy: http://numpy.scipy.org/ and Python Imaging Library: http://www.pythonware.com/products/pil/ """ # Copyright (c) 2009 Darcy Mason, Adit Panchal # This file is part of pydicom, relased under an MIT license. # See the file LICENSE included with this distribution, also # available at https://github.com/pydicom/pydicom # Based on image.py from pydicom version 0.9.3, # LUT code added by Adit Panchal # Tested on Python 2.5.4 (32-bit) on Mac OS X 10.6 # using numpy 1.3.0 and PIL 1.1.7b1 have_PIL = True try: import PIL.Image, PIL.ImageOps from pydicom.pixel_data_handlers.util import apply_voi_lut, apply_modality_lut except ImportError: have_PIL = False have_numpy = True try: import numpy as np except ImportError: have_numpy = False def get_LUT_value(data, window, level): """Apply the RGB Look-Up Table for the given data and window/level value.""" if not have_numpy: raise ImportError("Numpy is not available." "See http://numpy.scipy.org/" "to download and install") return np.piecewise(data, [data <= (level - 0.5 - (window - 1) / 2), data > (level - 0.5 + (window - 1) / 2)], [0, 255, lambda data: ((data - (level - 0.5)) / (window - 1) + 0.5) * (255 - 0)]) def get_PIL_image(dataset): """Get Image object from Python Imaging Library(PIL)""" if not have_PIL: raise ImportError("Python Imaging Library is not available. " "See http://www.pythonware.com/products/pil/ " "to download and install") if ('PixelData' not in dataset): raise TypeError("Cannot show image -- DICOM dataset does not have " "pixel data") # Apply modality lut array = apply_modality_lut(dataset.pixel_array, dataset) # voi lut doesn't seem to help #array = apply_voi_lut(array, dataset) # can only apply LUT if these window info exists if ('WindowWidth' not in dataset) or ('WindowCenter' not in dataset): bits = dataset.BitsAllocated samples = dataset.SamplesPerPixel if bits == 8 and samples == 1: mode = "L" elif bits == 8 and samples == 3: mode = "RGB" elif bits == 16: # not sure about this -- PIL source says is 'experimental' # and no documentation. Also, should bytes swap depending # on endian of file and system?? mode = "I;16" else: raise TypeError("Don't know PIL mode for %d BitsAllocated " "and %d SamplesPerPixel" % (bits, samples)) # PIL size = (width, height) size = (dataset.Columns, dataset.Rows) array = apply_modality_lut(dataset.pixel_array, dataset) array = apply_voi_lut(array, dataset) if dataset["PhotometricInterpretation"].value == "MONOCHROME1": # We need to invert array = np.invert(array) pixel_data = array.tostring() # Recommended to specify all details # by http://www.pythonware.com/library/pil/handbook/image.htm im = PIL.Image.frombuffer(mode, size, pixel_data, "raw", mode, 0, 1) else: ew = dataset['WindowWidth'] ec = dataset['WindowCenter'] ww = int(ew.value[0] if ew.VM > 1 else ew.value) wc = int(ec.value[0] if ec.VM > 1 else ec.value) ## Convert mode to L since LUT has only 256 values: ## http://www.pythonware.com/library/pil/handbook/image.htm image = get_LUT_value(array, ww, wc) im = PIL.Image.fromarray(image).convert('L') return im def show_PIL(dataset): """Display an image using the Python Imaging Library (PIL)""" im = get_PIL_image(dataset) im.show()