Skip to content

First steps

Installation

PyGRPM contains many sub-modules, and many of them need optional dependencies. To install the minimal version, use:

$ pip install pygrpm

Or to be sure to have all optional dependencies, use:

$ pip install pygrpm[all]

Getting started

Available modules

Making SR (Structured Report)

The creation of DICOM SR (Structured Report) has been simplified thanks to the pygrpm.dicom.sr.make_sr() and pygrpm.dicom.sr.make_sr_from_text() functions. Note that the SRBuilder class could allow a more refined/controlled creation.

To create an SR from an arbitrary string, use:

from pygrpm.dicom.sr import make_sr_from_text

sr = make_sr_from_text('my text', ['ref_series/ct-1.dcm', 'ref_series/ct-2,dcm'])

sr  # Is a pydicom.FileDataset
sr.ContentSequence[0].TextValue  # Contains the text
sr.ReferencedInstanceSequence  # Contains reference values

To use a custom content sequence (i.e: a specific structure):

from pygrpm.dicom.sr import make_sr

# The content sequence can be basically anything if it respects the DICOM standard.
# The user that want a specific structure is invited to read on SR in the DICOM standard.
content_sequence = {
    'ValueType': 'CONTAINER',
    'ConceptNameCodeSequence': {'CodeValue': 'DOC', 'CodeMeaning': 'Document', 'CodingSchemeDesignator': 'DCM'},
    'ContinuityOfContent': 'SEPARATE',
    'Value': [
        {
            'RelationshipType': 'HAS PROPERTIES',
            'ValueType': 'TEXT',
            'ConceptNameCodeSequence': {'CodeValue': '113012',
                                        'CodeMeaning': 'Key Object Description',
                                        'CodingSchemeDesignator': 'DCM'},
            'Value': 'Some text',
        }
    ],
},

sr = make_sr(content_sequence, ['ref_series/ct-1.dcm', 'ref_series/ct-2,dcm'])

sr  # Is a pydicom.FileDataset
sr.ContentSequence  # Correspond to the given content sequence
sr.ReferencedInstanceSequence  # Contains reference values

Users who wish to have more information on the creation of SR are invited to read documentation concerning SR builder, SR dose content sequences and DICOM part 03 section 17.

TG43

Python package to calculate the TG43 formalism based on xarray and the data from ESTRO.

Available seeds

  • SelectSeed
  • MicroSelectronV2
  • MBDCA
  • Flexisource

Usage

import matplotlib.pyplot as plt
from pygrpm.tg43 import Seed

seed = Seed("MBDCA")
print(seed.gL)
print(seed.F)

# Display dosimetric grid
grid = seed.grid()
# Plot arbitrary 100th slice
plt.imshow(grid.data[:, :, 100])
plt.show()

# Plot mean dose vs radius with std
mean = seed.get_mean(grid)
std = seed.get_std(grid)
plt.errorbar(mean["r"].data, mean.data, std.data, fmt='o')
plt.show()

Index tracker

Submodule that allows scrolling through slices of 3-D images using the matplotlib backend.

import matplotlib.pyplot as plt
import numpy
from pygrpm.visualization import IndexTracker

tracker = IndexTracker(
    *plt.subplot(),
    numpy.random.rand(512, 512, 10),
    ...
)
tracker.ax.set_title('My 3-D random image')
tracker.show()

See this for more information

NISTParser

A simple class to extract information from certain NIST webpages. At the time of writing this covers atomic and electronic cross-sections, material attenuation, as well as material composition.

Get cross sections

This method retrieves the desired cross-sections of an element at given energies on the NIST website in (barns/electron), barn=10^-24cm^2.

Simple use example:

import numpy as np
from pygrpm.material.nistparser import get_cross_sections

# Define the energies in KeV
# Numpy array is not mandatory, can be any sequence
energies = np.linspace(30, 200, 200)

# Prints the returned list
print(get_cross_sections("H", energies))

Electronic cross sections (get_electronic_cross_section) and attenuations (get_attenuations) are also available in the same way.

Get material compositions

This method is used to get and parse material composition from https://physics.nist.gov/cgi-bin/Star/compos.pl

Simple use example:

from pygrpm.material.nistparser import get_composition
from pygrpm.material.nistparser import NISTMaterials

# Prints the returned dictionary
print(get_composition(NISTMaterials.M3_WAX))

Note that get_composition expects the material to be of instance NISTMaterials

Acknowledgements

  • This submodule makes use of the HTMLTablePasrer built by Josua Schmid, further information can be found in the pygrpm.nistparser.nistparser.py file header.
  • This submodule is also dependent on the data provided by https://www.nist.gov/pml

Hounsfield conversion

A helper class meant to offer a quick and simple means to convert an HU array to density values based on a provided conversion table. Note that the conversion factors for HU to density are generally provided by the CT manufacturer. This class is currently only able to be read under csv format type.

Usage

Assuming the following sample data as ./curve.csv file

HU,Mass density [g/cm^3]
-1000,0.00121
-804,0.217
-505,0.508
-72,0.967
-32,0.99
7,1.018
44,1.061
52,1.071
254,1.159
4000,3.21

A call through the class can be made to rescale an arbitrary array of Hounsfield unit to density values.

import numpy as np
from pygrpm.material.hounsfield_conversion import ConvertHUToDensity

fithu = ConvertHUToDensity()
my_curve = fithu.load_curve_csv("./curve.csv")

# Note that setting plot to True generates a matplotlib plot of the curve fitting
data, fit_params, labels = fithu.fit_curve(my_curve, plot=True)
# Fit returns unused in this example

my_real_image = np.array([-980, -1000., -823., 1, 20, 700, 2900])
densities = fithu.apply_fit(my_real_image, my_curve)

print(densities)  # [0.02702014 0.00652871 0.18787786 1.0291972 1.03955733 1.41034076, 2.60993423]

TG263

Validating the structure name

from pygrpm.tg263.nomenclature import is_structure_name_allowed

result = is_structure_name_allowed('Prostate')
# Result is `True`

This will simply specify if the parameter string is an allowed structure Primary Name in the TG263 structure database.

Finding a structure

from pygrpm.tg263.nomenclature import find_structures

result = find_structures('SpinalCord')

This will print all information on every structure that as a TG263 primary name matching the parameter string. Structure information is return in dictionary format, and all structures are returned into a list.

Printing structure info

from pygrpm.tg263.nomenclature import print_structure_info

print_structure_info("<structure_to_print>")

This will print relevant structure information on the structure given as a parameter.

Validating a structure's format

from pygrpm.tg263.nomenclature import is_structure_valid

validation, log = is_structure_valid("<structure_to_validate>")

This will return a boolean (validation) wheiter the structure is valid. If not, the log will explain more precisely the nature of the problem.

DicomReader

A helper class meant to ingest a given folder of Dicom files and sort them into studies and series. This is aided with the DicomSeries and DicomStudy dataclasses.

Basic Usage

The class constructor is to be fed a system path for a folder containing, under any folder structure, the desired Dicom files.

from pygrpm.dicom import DicomReader

my_path = "/path/to/dicoms/folder/"
reader = DicomReader(my_path)

reader.studies # list of DicomStudy classes
reader.studies[0].series # list of DicomSeries classes

Dicoms can also be fed through unsorted through a generator with the following

from pygrpm.dicom import DicomReader

my_path = "/path/to/dicoms/folder/"
reader = DicomReader(my_path)

for study in reader.yield_dicoms():
    do_something(study)
DicomReader

There are a few helper methods available. Notably <reader>.get_study(<study_UID>) and <reader>.get_series(<study_UID>, <series_UID>) for easy access to given series or studies.

DicomStudy

It is also possible to filter a study by present modalities, <study>.filter_by_modality("CT") which would return a list of DicomSeries dataclasses of modality "CT".

DicomSeries

The DicomSeries dataclass offers easy access to the first instance of the series through <series>.first_instance as sorted by Z-slice position. Custom sorting is also available through <series>.sort_instances_by_tag(<dicom_tag>).

<series>.numpy returns a constructed numpy array from multiple 2D PixelData components as in the case of CT images, or fast-forwards pydicom's pixel_array in the case of 3D arrays such as in RTDose or RTStruct.

The pydicom FileDatasets are available within their respective DicomSeries dataclass within the <series>.instances list.

Egsphant Generator

A class designed to transform data parsed from a CT volume in numpy format, and provide an egsphant type text file.

Usage

The following example will assume that the user has already determined volume center, pixel spacing, and stacked the CT volume in a 3D numpy array. Note that the volume is expected to be in HU values for proper material assignment.

import numpy as np
from pygrpm.geometry.egsphant import make_egsphant_from_numpy

spacing = np.array(...)
center = np.array(...)
volume = np.array(...)  # Assuming slices in last position

# Select built-in materials list CT01 and specify slices in last axis
myegs = make_egsphant_from_numpy(volume, spacings=spacing, center=center,
                                 materials="CT01", slice_axis=-1)
# Can see the full string contents directly if desired
contents = myegs.content

myegs.write_to_file("./path/to/myvolume.egsphant")

Individual sections of the egsphant can also be accessed via the header, voxel_position_string, material_string, and density_string properties of the provided Egsphant class.

Acknowledgements

This work is a basic implementation of the TG263 (https://www.aapm.org/pubs/reports/RPT_263.pdf)

The allowed structure names (and corresponding information) were taken from https://www.aapm.org/pubs/reports/RPT_263_Supplemental/ .

The initial allowed structure names were taken from the ESAPIX project (https://github.com/rexcardan/ESAPIX), made by Rex Cardan. The ESAPIX license is included in the LICENSE file.