# Working with Pixel Data

How to work with pixel data in pydicom.

## Introduction

Many DICOM SOP classes contain bulk pixel data, which is usually used to represent one or more image frames (although other types of data are possible). In these SOP classes the pixel data is (almost) always contained in the (7FE0,0010) Pixel Data element. The only exception to this is Parametric Map Storage which may instead contain data in the (7FE0,0008) Float Pixel Data or (7FE0,0009) Double Float Pixel Data elements.

Note

In the following the term pixel data will be used to refer to the bulk data from Pixel Data, Float Pixel Data and Double Float Pixel Data elements. While the examples use `PixelData`, `FloatPixelData` or `DoubleFloatPixelData` could also be used interchangeably provided the dataset contains the corresponding element.

By default pydicom reads in pixel data as the raw bytes found in the file:

    >>> from pydicom import dcmread, examples
    >>> path = examples.get_path("mr")  # The path to the examples.mr dataset
    >>> ds = dcmread(path)
    >>> ds.PixelData
    b'\x89\x03\xfb\x03\xcb\x04\xeb\x04\xf9\x02\x94\x01\x7f...

`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

> - The image data may be encoded using one of the available compression standards (`1.2.840.10008.1.2.4.50` JPEG Baseline, `1.2.840.10008.1.2.5` RLE Lossless, etc). Encoded image data will also be encapsulated and each encapsulated image frame may be broken up into one or more fragments.

Because of the complexity in interpreting the pixel data, pydicom provides an easy way to get it in a convenient form: `Dataset.pixel_array`.

## `Dataset.pixel_array`

Warning

`Dataset.pixel_array` requires NumPy.

`Dataset.pixel_array` returns a `numpy.ndarray` containing the pixel data:

    >>> arr = ds.pixel_array
    >>> arr
    array([[ 905, 1019, 1227, ...,  302,  304,  328],
           [ 628,  770,  907, ...,  298,  331,  355],
           [ 498,  566,  706, ...,  280,  285,  320],
           ...,
           [ 334,  400,  431, ..., 1094, 1068, 1083],
           [ 339,  377,  413, ..., 1318, 1346, 1336],
           [ 378,  374,  422, ..., 1369, 1129,  862]], dtype=int16)
    >>> arr.shape
    (64, 64)

If the pixel data is compressed then `pixel_array` will return the uncompressed data, provided the dependencies of the required pixel data decoder have been met. See handling compressed image data for more information.

NumPy can be used to modify the data, but if the changes are to be saved, they must be written back to the dataset’s `PixelData` element.

Warning

Converting data from an `ndarray` back to `bytes` may not be as straightforward as in the following example, particularly for multi-planar images or where compression is required.

    # example: zero anything < 300
    arr = ds.pixel_array
    arr[arr < 300] = 0
    ds.PixelData = arr.tobytes()
    ds.save_as("temp.dcm")

Some changes may require other DICOM tags to be modified. For example, if the image size is reduced (e.g. a 512x512 image is shrunk to 256x256) then `Rows` and `Columns` should be set appropriately. You must explicitly set these yourself; pydicom does not do so automatically.

See Downsize MRI image using pydicom for an example.

`pixel_array` can also be used to pass image data to graphics libraries for viewing. See Viewing Images for details.

## Color space

When using `pixel_array` with Pixel Data that has an (0028,0002) Samples per Pixel value of `3` then the returned pixel data will be in the color space as given by (0028,0004) Photometric Interpretation (e.g. `RGB`, `YBR_FULL`, `YBR_FULL_422`, etc).

pydicom offers a limited ability to convert between 8-bits/channel YBR and RGB color spaces through the `convert_color_space()` function. When changing the color space you should also change the value of Photometric Interpretation to match.

Note

See the DICOM Standard, Part 3, Section C.7.6.3.1 for more information about color spaces.

## Palette Color

Some DICOM datasets store their output image pixel values in a lookup table (LUT), where the values in Pixel Data are the index to a corresponding LUT entry. When a dataset’s (0028,0004) Photometric Interpretation value is `PALETTE COLOR` then the `apply_color_lut()` function can be used to apply a palette color LUT to the pixel data to produce an RGB image.

    from pydicom import examples
    from pydicom.pixels import apply_color_lut

    # Fetch an example PALETTE COLOR dataset
    ds = examples.palette_color
    arr = ds.pixel_array
    rgb = apply_color_lut(arr, ds)

It’s also possible to apply one of the DICOM well-known color palettes provided the bit-depth of the pixel data is 8-bit.

    from pydicom import examples
    from pydicom.pixels import apply_color_lut

    ds = examples.palette_color
    arr = ds.pixel_array
    # You could also use the corresponding well-known SOP Instance UID
    rgb = apply_color_lut(arr, palette='PET')

Note

See the DICOM Standard, Part 3, Annexes C.7.6.3 and C.7.9 for more information.

## Modality LUT or Rescale Operation

The DICOM Modality LUT module converts raw pixel data values to a specific (possibly unitless) physical quantity, such as Hounsfield units for CT. The `apply_modality_lut()` function can be used with an input array of raw values and a dataset containing a Modality LUT module to return the converted values. When a dataset requires multiple grayscale transformations, the Modality LUT transformation is always applied first.

    from pydicom import examples
    from pydicom.pixels import apply_modality_lut

    ds = examples.ct
    arr = ds.pixel_array
    hu = apply_modality_lut(arr, ds)

## VOI LUT or Windowing Operation

The DICOM VOI LUT module applies a VOI or windowing operation to input values. The `apply_voi_lut()` function can be used with an input array and a dataset containing a VOI LUT module to return values with applied VOI LUT or windowing. When a dataset contains multiple VOI or windowing views then a particular view can be returned by using the index keyword parameter.

When a dataset requires multiple grayscale transformations, then it’s assumed that the modality LUT or rescale operation has already been applied.

    from pydicom import examples
    from pydicom.pixels import apply_voi_lut

    ds = examples.overlay
    arr = ds.pixel_array
    out = apply_voi_lut(arr, ds, index=0)