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 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 are mean_hu and std; 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, and lin will all draw the HU linearity values.

  • hu draws the HU linearity image.

  • un draws the HU uniformity image.

  • sp draws the Spatial Resolution image.

  • lc draws the Low Contrast image (if applicable).

  • mtf draws the RMTF plot.

  • lin draws the HU linearity values. Used with delta.

  • prof draws the HU uniformity profiles.

  • side draws 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 func parameter is accepted for interface compatibility with CatPhanBase but 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, side_view_kwargs: dict[str, object] | None = None, **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.

side_view_kwargsdict, optional

Keyword arguments passed to the side-view heatmap. If omitted, the Helios default windowing is used.

kwargs

Additional keyword arguments to pass to the module plots.

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, side_view_kwargs: dict[str, object] | None = None, **plt_kwargs) Figure[source]

Plot the analyzed image

Parameters

show

Whether to show the image.

side_view_kwargs

Keywords to pass to the side-view imshow call. If omitted, the Helios default windowing is used.

plt_kwargs

Keywords to pass to matplotlib for figure customization.

plot_images(show: bool = True, side_view_kwargs: dict[str, object] | None = None, **plt_kwargs) dict[str, Figure][source]

Plot all the individual images separately

Parameters

show

Whether to show the images.

side_view_kwargs

Keywords to pass to the side-view imshow call. If omitted, the Helios default windowing is used.

plt_kwargs

Keywords to pass to matplotlib for figure customization.

save_images(directory: Path | str | None = None, to_stream: bool = False, side_view_kwargs: dict[str, object] | None = None, **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.

side_view_kwargs

Keywords to pass to the side-view imshow call. If omitted, the Helios default windowing is used.

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 for print(). If False return 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, deduplicated.

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, **kwargs) None

Plot a view of the scan from the side with lines showing detected module positions.

Parameters

axis: Axes

The axis to plot the scan to

kwargs

Arguments passed to the axis constructor.

plotly_side_view(show_legend: bool, **kwargs) Figure

Plot a view of the scan from the side with lines showing detected module positions.

Parameters

show_legend: bool

Whether to show the plot legend.

kwargs

Arguments passed to the axis constructor.

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: ResultBase

Top-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: BaseModel

This 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.

field mean_hu_water: float [Required]

The mean HU of the water ROI.

field mean_hu_plastic: float [Required]

The mean HU of the Plexiglass ROI.

field hu_difference: float [Required]

The difference in mean HU between Plexiglass and water.

field std_dev_water: float [Required]

The standard deviation of the water ROI.

pydantic model pylinac.helios.HeliosHighContrastModuleOutput[source]

Bases: BaseModel

This 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

field std_dev_1_6mm: float [Required]

The standard deviation of the 1.6mm high-contrast ROI.

field std_dev_1_3mm: float [Required]

The standard deviation of the 1.3mm high-contrast ROI.

field std_dev_1_0mm: float [Required]

The standard deviation of the 1.0mm high-contrast ROI.

field std_dev_0_8mm: float [Required]

The standard deviation of the 0.8mm high-contrast ROI.

pydantic model pylinac.helios.HeliosNoiseUniformityModuleOutput[source]

Bases: BaseModel

This 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.

field center_mean_hu: float [Required]

The mean HU of the center ROI.

field center_noise_std_dev: float [Required]

The standard deviation of the central noise ROI.

field three_oclock_mean_hu: float [Required]

The mean HU of the 3 o’clock ROI.

field twelve_oclock_mean_hu: float [Required]

The mean HU of the 12 o’clock ROI.

field average_outer_mean_hu: float [Required]

The average mean HU of the outer ROIs.

field center_outer_mean_difference: float [Required]

The difference between the center mean HU and the average outer mean HU.

pydantic model pylinac.helios.HeliosLowContrastMultiSliceModuleOutput[source]

Bases: BaseModel

Results 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 the mean and std HU 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.

field low_contrast_mean: float [Required]

Mean HU value across all low-contrast slices.

field low_contrast_std: float [Required]

Average standard deviation across all low-contrast slices.