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
- Hounsfield Conversion
- SR Builder
- Index Tracker
- NISTParser
- TG43 Calculations
- TG263 Structures
- Dicom Reader
- Eggs Phantom Generator
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 thepygrpm.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.