GE Helios CT Daily QA Phantoms¶
Overview¶
Added in version 3.43.
The Helios module provides routines for automatically analyzing DICOM images of the GE Helios CT Daily QA Phantom. It can load a folder or zip file of images, correcting for translational offsets.
The phantom is cylindrical with two active sections at fixed scan locations. All terminology and analysis methodology follow the GE Technical Reference Manual; see also the Algorithm section.
Section 1 — Plexiglass resolution block embedded in water.
Tests performed:
Contrast Scale
High Contrast Spatial Resolution (bar patterns)
Section 3 — Uniform water bath.
Tests performed:
Noise
Uniformity
Low Contrast
Typical Use¶
The GE Helios analysis follows a similar pattern of load / analyze / output pattern as the rest of the library. Unlike the CatPhan analysis, the phantoms and analyses are much more well-defined, so creating fully custom phantom configurations is not a primary use case. Individual analysis modules can still be subclassed to adjust ROI locations or other parameters; see Customizing Modules.
To use the Helios analysis, import the class:
from pylinac import GEHeliosCTDaily
And then load, analyze, and view the results:
Load images – Loading can be done with a directory or zip file:
helios_folder = r"C:/CT/Helios/" helios = GEHeliosCTDaily(helios_folder)
or load from zip:
helios_zip = r"C:/CT/Helios/helios.zip" helios = GEHeliosCTDaily.from_zip(helios_zip)
Analyze – Analyze the dataset:
helios.analyze()
View the results – Reviewing the results can be done in text or dict format as well as images:
# print text to the console print(helios.results()) # view analyzed image summary helios.plot_analyzed_image() # view images independently helios.plot_images() # save the images helios.save_analyzed_image("helios_summary.png") # or helios.save_images(directory="output/") # finally, save a PDF helios.publish_pdf("helios_report.pdf")
Customizing Modules¶
To customize aspects of the analysis modules, subclass the relevant module and set the attribute in the analysis class. E.g. to customize the “Contrast Scale” module:
from pylinac.helios import GEHeliosCTDaily, HeliosContrastScaleModule
class ContrastScaleModified(HeliosContrastScaleModule):
"""Custom location for the contrast scale ROIs"""
roi_settings = {
"Plexiglass": {"width": 20, "height": 20, "distance": 35, "angle": -135},
"Water": {"width": 20, "height": 20, "distance": 75, "angle": 90},
}
# now pass to the analysis class
class MyHelios(GEHeliosCTDaily):
contrast_scale_module = ContrastScaleModified
# use as normal
helios = MyHelios(...)
helios.analyze(...)
There are 4 modules in the analysis that can be overridden. The attribute name should stay the same but the name of the subclassed module can be anything as long as it subclasses the original module:
class GEHeliosCTDaily:
# overload these as you wish. The attribute name cannot change.
contrast_scale_module = HeliosContrastScaleModule
high_contrast_module = HeliosHighContrastModule
noise_uniformity_module = HeliosNoiseUniformityModule
low_contrast_multi_slice = HeliosLowContrastMultiSliceModule
Customizing module offsets¶
Customizing the module offsets in the Helios module is easier than for the CT module. To do so, simply override any relevant constant like so:
import pylinac.helios
pylinac.helios.SECTION_3_OFFSET_MM = 55
helios = pylinac.GEHeliosCTDaily(...) # will use offset above
Advanced Use¶
Using results_data¶
Using the Helios module in your own scripts? The analysis results they can be accessed
by using the results_data() method
which returns a GEHeliosResult instance.
Continuing from above:
data = helios.results_data()
data.contrast_scale.contrast_difference
# and more
# return as a dict
data_dict = helios.results_data(as_dict=True)
data_dict["contrast_scale"]["contrast_difference"]
Adjusting ROI Locations¶
To adjust ROI locations, see the sister section for CT analysis: Adjusting ROI locations.
Analysis Parameters¶
See pylinac.helios.GEHeliosCTDaily.analyze() for details.
See the RadMachine documentation for analysis parameter details.
Interpreting Results¶
The outcome from analyzing the phantom and calling .results_data()
is a GEHeliosResult instance. See the API
documentation below for details and also Exporting Results.
The outcome from analyzing the phantom in RadMachine will return an “Entire Result” as follows:
phantom_model: The model of the phantom used.phantom_roll_deg: The roll of the phantom in degrees.origin_slice: The slice number of the “origin” slice; for helios this is Section 1.num_images: The number of images in the passed dataset.contrast_scale: The results from the Contrast Scale module, with the following items:offset: Module offset in mm from origin.rois: The analyzed ROIs. Structure:{"data": {"mean_hu": {"Plexiglass": val, "Water": val}, "std": {"Plexiglass": val, "Water": val}}}.roi_settings: The ROI settings. The keys are the material names.
high_contrast: The results from the High Contrast Spatial Resolution module, with the following items:offset: Module offset in mm from origin.rois: The analyzed ROIs. The keys are the bar-pattern sizes (e.g."1.6mm","1.3mm","1.0mm","0.8mm"); the values are the standard deviation within each ROI.mtf_lp_mm: Relative MTF at each spatial frequency.
noise_uniformity: The results from the Noise & Uniformity module, with the following items:offset: Module offset in mm from origin.roi_settings: The ROI settings. The keys are the ROI locations.rois: The analyzed ROIs. Structure:{"mean_hu": {"Center": val, ...}, "std": {"Center": val, ...}}. Top-level keys aremean_huandstd; nested keys are the ROI location names.noise_center_std: The noise in the central ROI.mean_outer: Mean HU values of the outer ROIs.means_diff: Difference between the center ROI mean and the average of the edge ROIs.
low_contrast: The results from the Low Contrast Multi Slice with the following items:slices: Per-slice low contrast results keyed by slice name.mean: Mean HU value across all slices.std: Average standard deviation across all slices.
Algorithm¶
The Helios analysis is based on the GE Technical Reference Manual.
Analysis¶
Contrast Scale - The Contrast Scale is measured using two rectangular ROIs (10 mm) placed on the origin slice, one over the Plexiglass resolution block and one over the surrounding water.
High Contrast Spatial Resolution - Four rectangular ROIs are placed over the bar-pattern groups within the Plexiglass block. The standard deviation within each ROI quantifies how well the bars are resolved. A relative MTF curve is also calculated where each spatial frequency is normalized to the coarsest (1.6 mm) bar pattern.
Noise & Uniformity - On the uniform water slice a 25 mm center box ROI measures noise (standard deviation). A 15 mm center box ROI and two 15 mm edge ROIs measure uniformity.
Low Contrast - Starting on section 3 and for the 2 previous slices (for a total of 3 slices), a 15x15 grid with 5 mm cells is placed at the center of the phantom. The average of the mean HU and the average of the standard deviations are reported.
API Documentation¶
- class pylinac.helios.GEHeliosCTDaily(folderpath: str | Sequence[str] | Path | Sequence[Path] | Sequence[BytesIO], check_uid: bool = True, memory_efficient_mode: bool = False, is_zip: bool = False)[source]¶
Bases:
CatPhanBase,ResultsDataMixin[GEHeliosResult]Parameters¶
- folderpathstr, list of strings, or Path to folder
String that points to the CBCT image folder location.
- check_uidbool
Whether to enforce raising an error if more than one UID is found in the dataset.
- memory_efficient_modebool
Whether to use a memory efficient mode. If True, the DICOM stack will be loaded on demand rather than all at once. This will reduce the memory footprint but will be slower by ~25%. Default is False.
Raises¶
- NotADirectoryError
If folder str passed is not a valid directory.
- FileNotFoundError
If no CT images are found in the folder
- contrast_scale_module¶
alias of
HeliosContrastScaleModule
- high_contrast_module¶
alias of
HeliosHighContrastModule
- low_contrast_multi_slice¶
alias of
HeliosLowContrastMultiSliceModule
- noise_uniformity_module¶
alias of
HeliosNoiseUniformityModule
- plot_analyzed_subimage(*args, **kwargs)[source]¶
Plot a specific component of the CBCT analysis.
Parameters¶
- subimage{‘hu’, ‘un’, ‘sp’, ‘lc’, ‘mtf’, ‘lin’, ‘prof’, ‘side’}
The subcomponent to plot. Values must contain one of the following letter combinations. E.g.
linearity,linear, andlinwill all draw the HU linearity values.hudraws the HU linearity image.undraws the HU uniformity image.spdraws the Spatial Resolution image.lcdraws the Low Contrast image (if applicable).mtfdraws the RMTF plot.lindraws the HU linearity values. Used withdelta.profdraws the HU uniformity profiles.sidedraws the side view of the phantom with lines of the module locations.
- deltabool
Only for use with
lin. Whether to plot the HU delta or actual values.- showbool
Whether to actually show the plot.
- save_analyzed_subimage(*args, **kwargs)[source]¶
Save a component image to file.
Parameters¶
- filenamestr, file object
The file to write the image to.
- subimagestr
See
plot_analyzed_subimage()for parameter info.- deltabool
Only for use with
lin. Whether to plot the HU delta or actual values.
- analyze(x_adjustment: float | int = 0, y_adjustment: float | int = 0, angle_adjustment: float | int = 0, roi_size_factor: float | int = 1, scaling_factor: float | int = 1, origin_slice: int | None = None) None[source]¶
Analyze the GE Helios CT Daily phantom.
Parameters¶
- x_adjustmentfloat
Shift the phantom center in the x-direction (pixels).
- y_adjustmentfloat
Shift the phantom center in the y-direction (pixels).
- angle_adjustmentfloat
Add a rotational offset to the phantom roll (degrees).
- roi_size_factorfloat
Factor to scale ROI sizes. 1.0 = nominal.
- scaling_factorfloat
Factor to scale ROI distances from center. 1.0 = nominal.
- origin_sliceint or None
If given, use this slice index as the origin instead of auto-detecting it.
- localize(origin_slice: int | None = None) None[source]¶
Locate the phantom in the dataset.
This finds the phantom axis across all slices, identifies the Section 1 (Plexiglass block) origin slice, and sets the roll angle to zero (the phantom is bracket-mounted).
Parameters¶
- origin_sliceint or None
If given, skip auto-detection and use this index directly.
- find_origin_slice() int[source]¶
Find the Section 1 slice containing the Plexiglass block.
The algorithm searches all slices for the one with the highest pixel-value variance within the phantom boundary. Section 1 contains a Plexiglass block (~120 HU) embedded in water (~0 HU) which produces distinctly higher variance than the uniform water of Section 3.
Returns¶
- int
The slice index of the origin (Section 1) slice.
- find_phantom_roll(func: Callable | None = None) float[source]¶
Return the phantom roll angle.
The GE Helios phantom is bracket-mounted and does not rotate; its roll is always zero. The
funcparameter is accepted for interface compatibility withCatPhanBasebut is not used.Parameters¶
- funccallable or None
Unused. Present for API compatibility only.
Returns¶
- float
Always
0.0.
- plotly_analyzed_images(show: bool = True, show_colorbar: bool = True, show_legend: bool = True, **kwargs) dict[str, Figure][source]¶
Plot the analyzed set of images to Plotly figures.
Parameters¶
- showbool
Whether to show the plot.
- show_colorbarbool
Whether to show the colorbar on the plot.
- show_legendbool
Whether to show the legend on the plot.
- kwargs
Additional keyword arguments to pass to the plot.
Returns¶
- dict
A dictionary of the Plotly figures where the key is the name of the image and the value is the figure.
- plot_analyzed_image(show: bool = True, **plt_kwargs) Figure[source]¶
Plot the analyzed image
Parameters¶
- show
Whether to show the image.
- plt_kwargs
Keywords to pass to matplotlib for figure customization.
- plot_images(show: bool = True, **plt_kwargs) dict[str, Figure][source]¶
Plot all the individual images separately
Parameters¶
- show
Whether to show the images.
- plt_kwargs
Keywords to pass to matplotlib for figure customization.
- save_images(directory: Path | str | None = None, to_stream: bool = False, **plt_kwargs) list[Path | BytesIO][source]¶
Save separate images to disk or stream.
Parameters¶
- directory
The directory to write the images to. If None, will use current working directory
- to_stream
Whether to write to stream or disk. If True, will return streams. Directory is ignored in that scenario.
- plt_kwargs
Keywords to pass to matplotlib for figure customization.
- publish_pdf(filename: str | Path, notes: str | None = None, open_file: bool = False, metadata: dict | None = None, logo: Path | str | None = None) None[source]¶
Publish (print) a PDF containing the analysis and quantitative results.
Parameters¶
- filename(str, file-like object}
The file to write the results to.
- notesstr, list of strings
Text; if str, prints single line. If list of strings, each list item is printed on its own line.
- open_filebool
Whether to open the file using the default program after creation.
- metadatadict
Extra data to be passed and shown in the PDF. The key and value will be shown with a colon. E.g. passing {‘Author’: ‘James’, ‘Unit’: ‘TrueBeam’} would result in text in the PDF like: ————– Author: James Unit: TrueBeam ————–
- logo: Path, str
A custom logo to use in the PDF report. If nothing is passed, the default pylinac logo is used.
- results(as_str: bool = True) str | tuple[source]¶
Return the results of the analysis as a string. Use with print().
All scalar and per-ROI outputs produced by
_generate_results_data()are represented: phantom roll, contrast-scale per-ROI mean HU and standard deviation, contrast difference, high-contrast per-bar-pattern ROI standard deviation, MTF at every 10 % resolution step, per-slice low-contrast mean and standard deviation for each of the three analysis slices followed by the aggregate low-contrast mean and standard deviation, noise/uniformity per-position mean HU and standard deviation, noise center standard deviation, mean outer HU, and uniformity difference.Parameters¶
- as_strbool
If
True(default) return a single newline-joined string suitable forprint(). IfFalsereturn a tuple of individual result strings.
Returns¶
- str or tuple of str
The formatted results.
- property catphan_size: float¶
The expected size of the phantom in pixels, based on a 20cm wide phantom.
- clear_captured_warnings() None¶
Clear the list of captured warnings.
- find_phantom_axis()¶
We fit all the center locations of the phantom across all slices to a 1D poly function instead of finding them individually for robustness.
Normally, each slice would be evaluated individually, but the RadMachine jig gets in the way of detecting the HU module (🤦♂️). To work around that in a backwards-compatible way we instead look at all the slices and if the phantom was detected, capture the phantom center. ALL the centers are then fitted to a 1D poly function and passed to the individual slices. This way, even if one slice is messed up (such as because of the phantom jig), the poly function is robust to give the real center based on all the other properly-located positions on the other slices.
- classmethod from_demo_images()¶
Construct a CBCT object from the demo images.
- classmethod from_url(url: str, check_uid: bool = True)¶
Instantiate a CBCT object from a URL pointing to a .zip object.
Parameters¶
- urlstr
URL pointing to a zip archive of CBCT images.
- check_uidbool
Whether to enforce raising an error if more than one UID is found in the dataset.
- classmethod from_zip(zip_file: str | ZipFile | BinaryIO, check_uid: bool = True, memory_efficient_mode: bool = False)¶
Construct a CBCT object and pass the zip file.
Parameters¶
- zip_filestr, ZipFile
Path to the zip file or a ZipFile object.
- check_uidbool
Whether to enforce raising an error if more than one UID is found in the dataset.
- memory_efficient_modebool
Whether to use a memory efficient mode. If True, the DICOM stack will be loaded on demand rather than all at once. This will reduce the memory footprint but will be slower by ~25%. Default is False.
Raises¶
FileExistsError : If zip_file passed was not a legitimate zip file. FileNotFoundError : If no CT images are found in the folder
- get_captured_warnings() list[dict]¶
Retrieve the list of captured warnings.
- property mm_per_pixel: float¶
The millimeters per pixel of the DICOM images.
- property num_images: int¶
The number of images loaded.
- plot_side_view(axis: Axes) None¶
Plot a view of the scan from the side with lines showing detected module positions
- refine_origin_slice(initial_slice_num: int) int¶
Apply a refinement to the origin slice. This was added to handle the catphan 604 at least due to variations in the length of the HU plugs.
- results_data(as_dict: bool = False, as_json: bool = False, by_alias: bool = False, exclude: set[str] | None = None) T | dict | str¶
Present the results data and metadata as a dataclass, dict, or tuple. The default return type is a dataclass.
Parameters¶
- as_dictbool
If True, return the results as a dictionary.
- as_jsonbool
If True, return the results as a JSON string. Cannot be True if as_dict is True.
- by_aliasbool
If True, use the alias names of the dataclass fields. These are generally the more human-readable names.
- excludeset
A set of fields to exclude from the results data.
- save_analyzed_image(filename: str | Path | BinaryIO, **kwargs) None¶
Save the analyzed summary plot.
Parameters¶
- filenamestr, file object
The name of the file to save the image to.
- kwargs :
Any valid matplotlib kwargs.
- to_quaac(path: str | Path, performer: User, primary_equipment: Equipment, format: Literal['json', 'yaml'] = 'yaml', attachments: list[Attachment] | None = None, overwrite: bool = False, **kwargs) None¶
Write an analysis to a QuAAC file. This will include the items from results_data() and the PDF report.
Parameters¶
- pathstr, Path
The file to write the results to.
- performerUser
The user who performed the analysis.
- primary_equipmentEquipment
The equipment used in the analysis.
- format{‘json’, ‘yaml’}
The format to write the file in.
- attachmentslist of Attachment
Additional attachments to include in the QuAAC file.
- overwritebool
Whether to overwrite the file if it already exists.
- kwargs
Additional keyword arguments to pass to the Document instantiation.
- pydantic model pylinac.helios.GEHeliosResult[source]¶
Bases:
ResultBaseTop-level results for the GE Helios CT Daily phantom analysis.
This class should not be called directly. It is returned by the
results_data()method.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- field phantom_model: str [Required]¶
The phantom model name.
- field phantom_roll_deg: float [Required]¶
The roll of the phantom in degrees.
- field origin_slice: int [Required]¶
The slice index of the origin (Section 1) slice.
- field num_images: int [Required]¶
The number of images in the dataset.
- field contrast_scale: HeliosContrastScaleModuleOutput [Required]¶
The results of the Contrast Scale test.
- field high_contrast: HeliosHighContrastModuleOutput [Required]¶
The results of the High Contrast Spatial Resolution test.
- field low_contrast: HeliosLowContrastMultiSliceModuleOutput [Required]¶
The results of the Low Contrast Detectability multi-slice test.
- field noise_uniformity: HeliosNoiseUniformityModuleOutput [Required]¶
The results of the Noise & Uniformity test.
- pydantic model pylinac.helios.HeliosContrastScaleModuleOutput[source]¶
Bases:
BaseModelThis class should not be called directly. It is returned by the
results_data()method.Use the following attributes as normal class attributes.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- field offset: float [Required]¶
The offset of this module slice from the origin slice in mm.
- field roi_settings: dict [Required]¶
The ROI settings. The keys are the material names.
- field rois: dict [Required]¶
The analyzed ROIs.
- pydantic model pylinac.helios.HeliosHighContrastModuleOutput[source]¶
Bases:
BaseModelThis class should not be called directly. It is returned by the
results_data()method.Use the following attributes as normal class attributes.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- field offset: float [Required]¶
The offset of this module slice from the origin slice in mm.
- field rois: dict [Required]¶
The analyzed ROIs.
- field mtf_lp_mm: dict[int, float] [Required]¶
A key-value pair of the MTF. The key is the relative resolution in % and the value is the lp/mm at that resolution
- pydantic model pylinac.helios.HeliosNoiseUniformityModuleOutput[source]¶
Bases:
BaseModelThis class should not be called directly. It is returned by the
results_data()method.Use the following attributes as normal class attributes.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- field offset: float [Required]¶
The offset of this module slice from the origin slice in mm.
- field roi_settings: dict [Required]¶
The ROI settings. The keys are the ROI locations.
- field rois: dict [Required]¶
The analyzed ROIs.
- field noise_center_std: float [Required]¶
The noise in the central ROI
- field mean_outer: float [Required]¶
Mean HU values of the outer ROIs.
- field means_diff: float [Required]¶
Difference between the center ROI mean and the average of the edge ROIs.
- pydantic model pylinac.helios.HeliosLowContrastMultiSliceModuleOutput[source]¶
Bases:
BaseModelResults for the multi-slice Low Contrast Detectability analysis.
This class should not be called directly. It is returned by the
results_data()method. Use the following attributes as normal class attributes.Attributes¶
- slicesdict[str, HeliosLowContrastModuleOutput]
Per-slice results keyed by slice name (
"slice_1","slice_2","slice_3"). Each value contains themeanandstdHU statistics for that individual slice.- meanfloat
Mean HU value averaged across all three slices.
- stdfloat
Average standard deviation across all three slices.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- field slices: dict[str, HeliosLowContrastModuleOutput] [Required]¶
Per-slice low contrast results keyed by slice name.
- field mean: float [Required]¶
Mean HU value across all slices.
- field std: float [Required]¶
Average standard deviation across all slices.