Pylinac provides TG-142 quality assurance (QA) tools to Python programmers as well as non-programmers in the field of therapy medical physics.
Pylinac contains high-level modules for automatically analyzing images and data generated by linear accelerators. Most scripts can be utilized with <10 lines of code. The built-in GUI is also straight-forward although programming experience may help identify errors.
The library also contains lower-level hackable modules & tools for creating your own image analysis algorithms.
The major features of the entire package include:
- GUI for easy use
- PDF report generation for solid documentation
- Automatic phantom registration even if you don’t set up your phantom perfect
- Image loading from file, ZIP archives, or URLs
- Directory analyzer with file retrieval (e.g. autogenerated Trajectory logs) with automatic analysis and report generation
To get started, install the package, run the demos, view the API docs, and learn the module design, visit the Full Documentation on Read The Docs.
pip, just check the dependencies first:
$ pip install pylinac
See the Installation page for further details.
Tools/Quick Start Guide¶
Below are the high-level tools currently available:
- GUI -
A simple but effective GUI is available for those who are less interested in programming. Start the GUI from Python:
import pylinac pylinac.gui() # that simple
- Continuous & One-Time Directory Analysis -
True, one-time “set it and forget it” service that analyzes any file that enters the folder. Files with keywords and certain file types are analyzed automatically. You can also specify “source” folders to pull new files from (e.g. a Varian shared drive holding Trajectory logs or QA images).
- Easy: Use filenames to specify analysis types - Just rename the file to contain a keyword (which are customizable) and move it to the directory!
- Easier: Use a classifier to identify images - The epitome of lazy! Certain analyses don’t even need to have a keyword if you choose to use the automatic classifier. E.g. a picket fence and starshot image can be differentiated and analyzed according to their own modules, with no extra work!
After installation, run the watcher script:
from pylinac import process process("dir/to/watch")
And that’s it! No code to write; no fuss, no muss. Analysis settings are customizable and includes optional email service upon analysis.
- TG-51 & TRS-398 Absolute Dose Calibration -
Input the raw data and pylinac can calculate either individual values (kQ, PDDx, Pion, etc) or use the provided classes to input all measurement data and have it calculate all factors and dose values automatically.
from pylinac import tg51, trs398 ENERGY = 6 TEMP = 22.1 PRESS = tg51.mmHg2kPa(755.0) CHAMBER = '30013' # PTW P_ELEC = 1.000 ND_w = 5.443 # Gy/nC MU = 200 CLINICAL_PDD = 66.5 tg51_6x = tg51.TG51Photon( unit='TrueBeam1', chamber=CHAMBER, temp=TEMP, press=PRESS, n_dw=ND_w, p_elec=P_ELEC, measured_pdd10=66.4, lead_foil=None, clinical_pdd10=66.5, energy=ENERGY, voltage_reference=-300, voltage_reduced=-150, m_reference=(25.65, 25.66, 25.65), m_opposite=(25.64, 25.65, 25.65), m_reduced=(25.64, 25.63, 25.63), mu=MU, tissue_correction=1.0 ) # Done! print(tg51_6x.dose_mu_dmax) # examine other parameters print(tg51_6x.pddx) print(tg51_6x.kq) print(tg51_6x.p_ion) # change readings if you adjust output tg51_6x.m_reference_adjusted = (25.44, 25.44, 25.43) # print new dose value print(tg51_6x.dose_mu_dmax_adjusted) # generate a PDF for record-keeping tg51_6x.publish_pdf('TB1 6MV TG-51.pdf', notes=['My notes', 'I used Pylinac to do this; so easy!'], open_file=False) # TRS-398 is very similar and just as easy!
- Planar Phantom Analysis (Leeds TOR, StandardImaging QC-3, Las Vegas) -
The planar imaging module analyzes 2D phantoms. Currently the LeedsTOR kV phantom, Standard Imaging QC-3 MV phantom, and Las Vegas phantom are supported, and more phantoms are in the works!
- Automatic phantom localization - Set up your phantom any way you like; automatic positioning, angle, and inversion correction mean you can set up how you like, nor will setup variations give you headache.
- High and low contrast determination - Analyze both low and high contrast ROIs. Set thresholds as you see fit.
from pylinac import LeedsTOR, StandardImagingQC3, LasVegas leeds = LeedsTOR("my_leeds.dcm") leeds.analyze() leeds.plot_analyzed_image() leeds.publish_pdf() qc3 = StandardImagingQC3("my_qc3.dcm") qc3.analyze() qc3.plot_analyzed_image() qc3.publish_pdf('qc3.pdf') lv = LasVegas("my_lv.dcm") lv.analyze() lv.plot_analyzed_image() lv.publish_pdf('lv.pdf', open_file=True) # open the PDF after publishing
- Winston-Lutz Analysis -
The Winston-Lutz module analyzes EPID images taken of a small radiation field and BB to determine the 2D distance from BB to field CAX. Additionally, the isocenter size of the gantry, collimator, and couch can all be determined without the BB being at isocenter. Analysis is based on Winkler et al and Du et al.
- Couch shift instructions - After running a WL test, get immediate feedback on how to shift the couch. Couch values can also be passed in and the new couch values will be presented so you don’t have to do that pesky conversion. “Do I subtract that number or add it?”
- Automatic field & BB positioning - When an image or directory is loaded, the field CAX and the BB are automatically found, along with the vector and scalar distance between them.
- Isocenter size determination - Using backprojections of the EPID images, the 3D gantry isocenter size and position can be determined independent of the BB position. Additionally, the 2D planar isocenter size of the collimator and couch can also be determined.
- Image plotting - WL images can be plotted separately or together, each of which shows the field CAX, BB and scalar distance from BB to CAX.
- Axis deviation plots - Plot the variation of the gantry, collimator, couch, and EPID in each plane as well as RMS variation.
- File name interpretation - Rename DICOM filenames to include axis information for linacs that don’t include such information in the DICOM tags. E.g. “myWL_gantry45_coll0_couch315.dcm”.
from pylinac import WinstonLutz wl = WinstonLutz("wl/image/directory") # images are analyzed upon loading wl.plot_summary() print(wl.results()) wl.publish_pdf('my_wl.pdf')
- Starshot Analysis -
- Analyze scanned film images, single EPID images, or a set of EPID images - Any image that you can load in can be analyzed, including 1 or a set of EPID DICOM images and films that have been digitally scanned.
- Any image size - Have machines with different EPIDs? Scanned your film at different resolutions? No problem.
- Dose/OD can be inverted - Whether your device/image views dose as an increase in value or a decrease, pylinac will detect it and invert if necessary.
- Automatic noise detection & correction - Sometimes there’s dirt on the scanned film; sometimes there’s a dead pixel on the EPID. Pylinac will detect these spurious noise signals and can avoid or account for them.
- Accurate, FWHM star line detection - Pylinac uses not simply the maximum value to find the center of a star line, but analyzes the entire star profile to determine the center of the FWHM, ensuring small noise or maximum value bias is avoided.
- Adaptive searching - If you passed pylinac a set of parameters and a good result wasn’t found, pylinac can recover and do an adaptive search by adjusting parameters to find a “reasonable” wobble.
from pylinac import Starshot star = Starshot("mystarshot.tif") star.analyze(radius=0.75, tolerance=1.0, fwhm=True) print(star.results()) # prints out wobble information star.plot_analyzed_image() # shows a matplotlib figure star.publish_pdf() # publish a PDF report
- VMAT QA -
The VMAT module consists of two classes:
DRMLC, which are capable of loading an EPID DICOM Open field image and MLC field image and analyzing the images according to the Varian RapidArc QA tests and procedures, specifically the Dose-Rate & Gantry-Speed (DRGS) and MLC speed (MLCS) tests.
- Do both tests - Pylinac can handle either DRGS or DRMLC tests.
- Adjust for offsets - Older VMAT patterns were off-center. Pylinac will find the field regardless.
from pylinac import DRGS, DRMLC drgs = DRGS(image_paths=["path/to/DRGSopen.dcm", "path/to/DRGSdmlc.dcm"]) drgs.analyze(tolerance=1.5) print(drgs.results()) # prints out ROI information drgs.plot_analyzed_image() # shows a matplotlib figure drgs.publish_pdf('mydrgs.pdf') # generate a PDF report
- CT & CBCT QA -
The CBCT module automatically analyzes DICOM images of a CatPhan 504, 503, or 600 acquired when doing CT or CBCT quality assurance. It can load a folder or zip file that the images are in and automatically correct for phantom setup in 6 axes. It can analyze the HU regions and image scaling (CTP404), the high-contrast line pairs (CTP528) to calculate the modulation transfer function (MTF), and the HU uniformity (CTP486) on the corresponding slice.
- Automatic phantom registration - Your phantom can be tilted, rotated, or translated–pylinac will register the phantom.
- Automatic testing of all major modules - Major modules are automatically registered and analyzed.
- Any scan protocol - Scan your CatPhan with any protocol; or even scan it in a regular CT scanner. Any field size or field extent is allowed.
from pylinac import CatPhan504, CatPhan503, CatPhan600, CatPhan604 # for this example, we'll use the CatPhan504 cbct = CatPhan504("my/cbct_image_folder") cbct.analyze(hu_tolerance=40, scaling_tolerance=1, thickness_tolerance=0.2, low_contrast_threshold=1) print(cbct.results()) cbct.plot_analyzed_image() cbct.publish_pdf('mycbct.pdf')
- Log Analysis -
The log analyzer module reads and parses Varian linear accelerator machine logs, both Dynalogs and Trajectory logs. The module also calculates actual and expected fluences as well as performing gamma evaluations. Data is structured to be easily accessible and easily plottable.
Unlike most other modules of pylinac, the log analyzer module has no end goal. Data is parsed from the logs, but what is done with that info, and which info is analyzed is up to the user.
- Analyze Dynalogs or Trajectory logs - Either platform is supported. Tlog versions 2.1 and 3.0 supported.
- Save Trajectory log data to CSV - The Trajectory log binary data format does not allow for easy export of data. Pylinac lets you do that so you can use Excel or other software that you use with Dynalogs.
- Plot or analyze any axis - Every data axis can be plotted: the actual, expected, and even the difference.
- View actual or expected fluences & calculate gamma - View fluences and gamma maps for any log.
- Anonymization - Anonymize your logs so you can share them with others.
from pylinac import load_log tlog = load_log("tlog.bin") # after loading, explore any Axis of the Varian structure tlog.axis_data.gantry.plot_actual() # plot the gantry position throughout treatment tlog.fluence.gamma.calc_map(doseTA=1, distTA=1, threshold=10, resolution=0.1) tlog.fluence.gamma.plot_map() # show the gamma map as a matplotlib figure tlog.publish_pdf() # publish a PDF report dlog = load_log("dynalog.dlg") ...
- Picket Fence MLC Analysis -
The picket fence module is meant for analyzing EPID images where a “picket fence” MLC pattern has been made. Physicists regularly check MLC positioning through this test. This test can be done using film and one can “eyeball” it, but this is the 21st century and we have numerous ways of quantifying such data. This module attains to be one of them. It will load in an EPID dicom image and determine the MLC peaks, error of each MLC pair to the picket, and give a few visual indicators for passing/warning/failing.
- Analyze either HD or regular MLCs - Just pass a flag and tell pylinac whether it’s HD or not.
- Easy-to-read pass/warn/fail overlay - Analysis gives you easy-to-read tools for determining the status of an MLC pair.
- Any Source-to-Image distance - Whatever your clinic uses as the SID for picket fence, pylinac can account for it.
- Account for panel translation - Have an off-CAX setup? No problem. Translate your EPID and pylinac knows.
- Account for panel sag - If your EPID sags at certain angles, just tell pylinac and the results will be shifted.
from pylinac import PicketFence pf = PicketFence("mypf.dcm") pf.analyze(tolerance=0.5, action_tolerance=0.25) print(pf.results()) pf.plot_analyzed_image() pf.publish_pdf()
- Flatness & Symmetry Analysis -
Flatness & symmetry from a digital image such as EPID DICOM can easily by analyzed. The module contains built-in F&S equation definitions but is extensible to quickly create custom F&S equations.
- Analyze any digital image - JPG, TIFF, DICOM are all valid inputs.
- Built-in F&S equations - The common Elekta and Varian defintions are included
- Extensible F&S equations - Adding custom equations for F&S is straightforward
from pylinac import FlatSym fs = FlatSym(path="myFS.dcm") fs.analyze(flatness_method='varian', symmetry_method='varian', vert_position=0.5, horiz_position=0.5) # print results print(fs.results()) # publish a PDF file fs.publish_pdf(filename='myFS.pdf')
Contributions to pylinac can be many. The most useful things a non-programmer can contribute are images to analyze and bug reports. If you have VMAT images, starshot images, machine log files, CBCT DICOM files, or anything else you want analyzed, upload them privately here.
- Pylinac General Overview
- General Tips
- Calibration module documentation
- Starshot module documentation
- VMAT module documentation
- CatPhan module documentation
- Log Analyzer module documentation
- Picket Fence module documentation
- Winston-Lutz module documentation
- Planar Imaging module documentation
- Flatness/Symmetry module documentation
- Directory Watching
- Core Modules Documentation
- Hacking your own tools with Pylinac
- v 2.2.7
- V 2.2.6
- V 2.2.5
- V 2.2.4
- V 2.2.3
- V 2.2.2
- V 2.2.1
- V 2.2.0
- V 2.1.0
- V 2.0.0
- V 1.9.0
- v 1.8.0
- v 1.7.2
- V 1.7.0
- V 1.6.0
- V 1.5.6
- V 1.5.5
- V 1.5.4
- V 1.5.1-3
- V 1.5.0
- V 1.4.1
- V 1.4.0
- V 1.3.1
- V 1.3.0
- V 1.2.2
- V 1.2.1
- V 1.2.0
- V 1.1.1
- V 1.1.0
- V 1.0.3
- V 1.0.2
- V 1.0.1
- V 1.0.0
- V 0.9.1
- V 0.9.0
- V 0.8.2
- V 0.8.1
- V 0.8.0
- V 0.7.1
- V 0.7.0
- V 0.6.0
- V 0.5.1
- V 0.5.0
- V 0.4.1
- V 0.4.0
- V 0.3.0
- V 0.2.1
- V 0.2.0
- V 0.1.3
- V 0.1.2
- V 0.1.1
- V 0.1.0