pylinac Documentation



Similar documents
RapidArc QA Program in Prince of Wales Hospital. Michael L. M. Cheung, Physicist Prince of Wales Hospital Hong Kong

Post Treatment Log File Based QA Varian. Krishni Wijesooriya, PhD University of Virginia. D e p a r t m e n t of R a d i a t i o n O n c o l o g y

Exercise 0. Although Python(x,y) comes already with a great variety of scientic Python packages, we might have to install additional dependencies:

Multi-Channel Radiochromic Film Dosimetry. Adapted from A.Micke Spain, April 2014

Intro to scientific programming (with Python) Pietro Berkes, Brandeis University

CT RADIATION DOSE REPORT FROM DICOM. Frank Dong, PhD, DABR Diagnostic Physicist Imaging Institute Cleveland Clinic Foundation Cleveland, OH

Performance testing for Precision 500D Classical R/F System

Application Report. Propeller Blade Inspection Station

latest Release 0.2.6

ACR Triad Site Server Click Once Software System

Python for Series 60 Platform

Tom Wilson Product Marketing Manager Delivery Systems Varian Medical Systems International AG. CERN Accelerator School, May 2015

Gas Dynamics Prof. T. M. Muruganandam Department of Aerospace Engineering Indian Institute of Technology, Madras. Module No - 12 Lecture No - 25

Introduction to Python

Coding HTML Tips, Tricks and Best Practices

Getting Started with the Internet Communications Engine

5-Axis Test-Piece Influence of Machining Position

IntelliSpace PACS 4.4. Image Enabled EMR Workflow and Enterprise Overview. Learning Objectives

Analysis Programs DPDAK and DAWN

521466S Machine Vision Assignment #7 Hough transform

Autodesk Fusion 360: Assemblies. Overview

Dashboard Skin Tutorial. For ETS2 HTML5 Mobile Dashboard v3.0.2

Processing Data with rsmap3d Software Services Group Advanced Photon Source Argonne National Laboratory

kv-& MV-CBCT Imaging for Daily Localization: Commissioning, QA, Clinical Use, & Limitations

Hypercosm. Studio.

Our Department: structure and organization

The Whys, Hows and Whats of the Noise Power Spectrum. Helge Pettersen, Haukeland University Hospital, NO

Why HTML5 Tests the Limits of Automated Testing Solutions

International Year of Light 2015 Tech-Talks BREGENZ: Mehmet Arik Well-Being in Office Applications Light Measurement & Quality Parameters

3D SCANNERTM. 3D Scanning Comes Full Circle. s u n. Your Most Valuable QA and Dosimetry Tools A / B / C. The 3D SCANNER Advantage

Scan to Network and Scan to Network Premium. Administrator's Guide

GelAnalyzer 2010 User s manual. Contents

Data Crow Creating Reports

QUANTITATIVE IMAGING IN MULTICENTER CLINICAL TRIALS: PET

Django Two-Factor Authentication Documentation

Exiqon Array Software Manual. Quick guide to data extraction from mircury LNA microrna Arrays

The Concept(s) of Mosaic Image Processing. by Fabian Neyer

Availability of the Program A free version is available of each (see individual programs for links).

Setting Up Your Android Development Environment. For Mac OS X (10.6.8) v1.0. By GoNorthWest. 3 April 2012

KB COPY CENTRE. RM 2300 JCMB The King s Buildings West Mains Road Edinburgh EH9 3JZ. Telephone:

CEFNS Web Hosting a Guide for CS212

Cardiac Tool User s Guide. Cardiac Tool. Version 1.0

THE DOSIMETRIC EFFECTS OF

Petrel TIPS&TRICKS from SCM

SuiteCRM for Developers

Topographic Change Detection Using CloudCompare Version 1.0

Sonatype CLM Enforcement Points - Continuous Integration (CI) Sonatype CLM Enforcement Points - Continuous Integration (CI)

VISUAL ALGEBRA FOR COLLEGE STUDENTS. Laurie J. Burton Western Oregon University

A. OPENING POINT CLOUDS. (Notepad++ Text editor) (Cloud Compare Point cloud and mesh editor) (MeshLab Point cloud and mesh editor)

Files Used in this Tutorial

multimodality image processing workstation Visualizing your SPECT-CT-PET-MRI images

RADIATION THERAPY. Dosimetry Pioneers since 1922 NEW

Programming Languages CIS 443

So you want to create an a Friend action

Distance-Learning Remote Laboratories using LabVIEW

1-Step Appraisals Jewelry Appraisal Software

Available Update Methods

Mail Programming Topics

Applying a circular load. Immediate and consolidation settlement. Deformed contours. Query points and query lines. Graph query.

What is new or different in AppScan Enterprise v9.0.2 if you re upgrading from v

TSScan - Usage Guide. Usage Guide. TerminalWorks TSScan 2.5 Usage Guide. support@terminalworks.com

TUTORIAL 4 Building a Navigation Bar with Fireworks

1 ImageBrowser Software User Guide

Introducing Xcode Source Control

A System for Capturing High Resolution Images

MetaMorph Software Basic Analysis Guide The use of measurements and journals

Distributing forms and compiling forms data

Portal Connector Fields and Widgets Technical Documentation

Exercise 4 Learning Python language fundamentals

Programming Project 1: Lexical Analyzer (Scanner)

Pro/ENGINEER Wildfire 4.0 Basic Design

Applitools Eyes Web Application Guide

Using Image J to Measure the Brightness of Stars (Written by Do H. Kim)

Introduction. How does FTP work?

Generating Leads While You Sleep

Finite Element Modeling

#include <Gamer.h> Gamer gamer; void setup() { gamer.begin(); } void loop() {

Creating Interactive PDF Forms

Solutions to Exercises, Section 5.1

Braindumps.C questions

2013 Getting Started Guide

Definiens XD Release Notes

MLC Characteristics. Treatment Delivery Systems 2 Field Shaping; Design Characteristics and Dosimetry Issues. Presentation Outline

Glass coloured glass may pick up on scan. Top right of screen tabs: these tabs will relocate lost windows.

High-accuracy ultrasound target localization for hand-eye calibration between optical tracking systems and three-dimensional ultrasound

QA of intensity-modulated beams using dynamic MLC log files

SyncTool for InterSystems Caché and Ensemble.

Performance Monitoring using Pecos Release 0.1

Multi-Touch Control Wheel Software Development Kit User s Guide

Machine Tool Control. Besides these TNCs, HEIDENHAIN also supplies controls for other areas of application, such as lathes.

Java Language Tools COPYRIGHTED MATERIAL. Part 1. In this part...

CRASH COURSE PYTHON. Het begint met een idee

Instructional Design Framework CSE: Unit 1 Lesson 1

Python Loops and String Manipulation

Introduction to Python for Text Analysis

KNIME TUTORIAL. Anna Monreale KDD-Lab, University of Pisa

Librarian. Integrating Secure Workflow and Revision Control into Your Production Environment WHITE PAPER

We automatically generate the HTML for this as seen below. Provide the above components for the teaser.txt file.

Magento module Documentation

Transcription:

pylinac Documentation Release 1.3.1 James February 06, 2016

Contents 1 Pylinac General Overview 3 1.1 Intended Use............................................... 4 1.2 Philosophy................................................ 4 1.3 Algorithm Design Overview....................................... 5 1.4 Module Design.............................................. 5 2 Installation 7 2.1 I know Python already.......................................... 7 2.2 Dependencies............................................... 7 2.3 I m new to Python............................................ 8 3 Getting Started 9 3.1 Running a Demo............................................. 9 3.2 Loading in Images/Data......................................... 10 4 Starshot module documentation 11 4.1 Overview................................................. 11 4.2 Running the Demo............................................ 11 4.3 Image Acquisition............................................ 12 4.4 Typical Use................................................ 12 4.5 Algorithm................................................ 13 4.6 Troubleshooting............................................. 14 4.7 API Documentation........................................... 14 5 VMAT module documentation 21 5.1 Overview................................................. 21 5.2 Running the Demo............................................ 21 5.3 Image Acquisition............................................ 22 5.4 Naming Convention........................................... 22 5.5 Typical Use................................................ 22 5.6 Tips & Tricks............................................... 24 5.7 Algorithm................................................ 24 5.8 API Documentation........................................... 25 6 CBCT module documentation 31 6.1 Overview................................................. 31 6.2 Running the Demo............................................ 31 6.3 Typical Use................................................ 32 6.4 Algorithm................................................ 33 i

6.5 Troubleshooting............................................. 35 6.6 API Documentation........................................... 35 7 Log Analyzer module documentation 47 7.1 Overview................................................. 47 7.2 Concepts................................................. 47 7.3 Running the Demo............................................ 48 7.4 Loading Data............................................... 50 7.5 Working with the Data.......................................... 50 7.6 Converting Trajectory logs to CSV................................... 56 7.7 Anonymizing Logs............................................ 56 7.8 Batch Processing............................................. 57 7.9 API Documentation........................................... 57 8 Picket Fence module documentation 75 8.1 Overview................................................. 75 8.2 Concepts................................................. 75 8.3 Running the Demo............................................ 76 8.4 Acquiring the Image........................................... 78 8.5 Typical Use................................................ 78 8.6 Tips & Tricks............................................... 79 8.7 Algorithm................................................ 80 8.8 Troubleshooting............................................. 81 8.9 API Documentation........................................... 82 9 Winston-Lutz module documentation 87 9.1 Overview................................................. 87 9.2 Running the Demo............................................ 87 9.3 Image Acquisition............................................ 88 9.4 Coordinate Space............................................. 88 9.5 Typical Use................................................ 89 9.6 Algorithm................................................ 89 9.7 API Documentation........................................... 90 10 Planar Imaging module documentation 95 10.1 Overview................................................. 95 10.2 Leeds Phantom.............................................. 95 10.3 PipsPro Phantom............................................. 101 10.4 API Documentation........................................... 104 11 Flatness/Symmetry module documentation 107 11.1 Overview................................................. 107 11.2 Running the Demo............................................ 107 11.3 Typical Use................................................ 108 11.4 Analysis Definitions........................................... 109 11.5 Algorithm................................................ 109 11.6 API Documentation........................................... 110 12 Directory Watching 113 12.1 Setup................................................... 113 12.2 Tweaking................................................. 113 12.3 API Documentation........................................... 114 13 Core Modules Documentation 117 13.1 Image Module.............................................. 117 ii

13.2 Geometry Module............................................ 122 13.3 Profile Module.............................................. 124 13.4 I/O Module................................................ 131 13.5 ROI Module............................................... 132 13.6 Mask Module............................................... 133 13.7 Utilities Module............................................. 134 13.8 Decorators Module............................................ 134 14 Hacking your own tools with Pylinac 137 14.1 The image Module........................................... 137 14.2 The profile Module.......................................... 147 15 Troubleshooting 161 15.1 General.................................................. 161 15.2 Loading TIFF Files............................................ 161 16 Changelog 163 16.1 V 1.3.1.................................................. 163 16.2 V 1.3.0.................................................. 163 16.3 V 1.2.2.................................................. 163 16.4 V 1.2.1.................................................. 164 16.5 V 1.2.0.................................................. 164 16.6 V 1.1.1.................................................. 165 16.7 V 1.1.0.................................................. 165 16.8 V 1.0.3.................................................. 165 16.9 V 1.0.2.................................................. 165 16.10 V 1.0.1.................................................. 165 16.11 V 1.0.0.................................................. 166 16.12 V 0.9.1.................................................. 166 16.13 V 0.9.0.................................................. 166 16.14 V 0.8.2.................................................. 167 16.15 V 0.8.1.................................................. 167 16.16 V 0.8.0.................................................. 167 16.17 V 0.7.1.................................................. 168 16.18 V 0.7.0.................................................. 168 16.19 V 0.6.0.................................................. 169 16.20 V 0.5.1.................................................. 170 16.21 V 0.5.0.................................................. 170 16.22 V 0.4.1.................................................. 170 16.23 V 0.4.0.................................................. 170 16.24 V 0.3.0.................................................. 171 16.25 V 0.2.1.................................................. 171 16.26 V 0.2.0.................................................. 171 16.27 V 0.1.3.................................................. 171 16.28 V 0.1.2.................................................. 171 16.29 V 0.1.1.................................................. 172 16.30 V 0.1.0.................................................. 172 17 Indices and tables 173 Python Module Index 175 iii

iv

Welcome to Pylinac! This package is designed to help medical physicists perform linear accelerator quality assurance (QA), mostly from TG-142, easily and automatically. Pylinac contains a number of modules, each one addressing a common QA need. Code is compliant with Python 3, but not Python 2. You can read a bit about pylinac below, or jump right in by doing Installation and then Getting Started! If you have questions, head on over to the forum. Contents 1

2 Contents

CHAPTER 1 Pylinac General Overview 3

1.1 Intended Use Pylinac is intended to be used by two types of physicists: ones who know at least a bit of programming and those who know nothing about programming. For the first group, pylinac can be used within a Python environment to automate analysis of QA images. For the second group, pylinac is also implemented as a web app. 1.2 Philosophy Pylinac runs on a few philosophical principles: A given module should only address 1 overarching task. Using pylinac should require a minimal amount of code. The user should have to supply as little information as necessary to run an analysis. The underlying code of pylinac should be easy to understand. The joy of coding Python should be in seeing short, concise, readable classes that express a lot of action in a small amount of clear code not in reams of trivial code that bores the reader to death. Guido van Rossum 4 Chapter 1. Pylinac General Overview

1.3 Algorithm Design Overview Generally speaking, the design of algorithms should all follow the same guidelines and appear as similar as possible. Each module will outline its own specific algorithm in its documentation. Descriptions of algorithms are sorted into steps of the following: Allowances These describe what the pylinac algorithm can account for. Restrictions These are the things pylinac cannot do and must be addressed before the module can be properly used. Pre-Analysis Algorithm steps that prepare for the main algorithm sequence. Analysis The steps pylinac takes to analyze the image or data. Post-Analysis What pylinac does or can do after analysis, like showing the data or checking against tolerances. Algorithm steps should be expressible in a word or short phrase. Algorithm method names should be as similar as possible from module to module. 1.4 Module Design Pylinac has a handful of modules, but most of them work somewhat the same, so here we describe the general patterns you ll see when using pylinac. Each module has its own demonstration method(s) If you don t yet have an image or data and want to see how a module works you can run and inspect the code of the demo to get an idea. Most demo methods have a name like or starts with.run_demo(). Each module has similar load, analyze, and show methods and behavior The normal flow of a pylinac module use is to 1) Load the data in, 2) Analyze the data, and 3) Show the results. Most modules can be fully utilized in a few lines The whole point of pylinac is to automate and simplify the process of analyzing routine QA data. Thus, most routines can be written in a few lines. Each module gives a starting script in its documentation. 1.3. Algorithm Design Overview 5

6 Chapter 1. Pylinac General Overview

CHAPTER 2 Installation Installing pylinac is easy no matter your skill! Determine where you re at and then read the relevant section: 2.1 I know Python already Great! To get started, simply install pylinac via PyPI: pip install pylinac If you run into issues: Try upgrading pip all the way pip install pip --upgrade. If there are LAPACK/BLAS compilation errors from scipy/numpy install numpy and scipy using conda, and install pylinac using the --no-deps flag: pip install pylinac --no-deps. Finally, you can download the repo (from github) and run setup.py install in the root directory 2.2 Dependencies Pylinac, as a scientific package, has fairly standard scientific dependencies (>= means at least that version or newer): numpy >= 1.9 scipy >= 0.15 matplotlib >= 1.3.1 pydicom >= 0.9.9 Pillow >= 2.5 scikit-image >= 0.11 See the numpy/scipy installation instructions if you don t yet have them. Optional dependencies: watchdog >= 0.8 Needed if using the watcher script. See Directory Watching. mpld3 >= 0.2 For making interactive HTML plots. 7

2.3 I m new to Python That s okay! If you re not a programmer at all you ll have a few things to do to get up and running, but never fear. Using pylinac requires not just the base language Python, but a few dependencies as well. Since most physicists don t program, or if they do it s in MATLAB, this section will help jumpstart your use of not just pylinac but Python in general and all its wonderful goodness! Getting started with Python takes some work to get set up and running, but it s well worth the effort. Get a Distribution Stack Scientific computing with Python requires some specialized packages which require some specialized computing libraries. While it s possible you have those libraries (for some odd reason), it s not likely. Thus, it s often best to install the libraries pre-compiled. There are several options out there; I ll list just a few. Be sure to download the 3.x version, preferably the newest: Anaconda - Continuum Analytics provides this one-stop-shop for tons of scientific libraries in an easy to install format. Just download and run the installer. If you don t want to install all 200+ packages, a slimmer option exists: Miniconda, which only installs conda and python installation tools. You can then use conda to install packages individually. Here s the Anaconda quick start guide. Note: Unlike the other options, individual packages can be upgraded on demand using the conda tool. WinPython - (Windows only) This grassroots project functions similarly to Anaconda, where all packages are precompiled and run out of the box. There are no corporate sponsors for this project, so support is not guaranteed. See Scipy s Installation Options for more options. Warning: Python(x,y) is not yet available for Python 3, so don t choose this to try running pylinac. Note: If this is the first/only Python distribution you ll be using it d be a good idea to activate it when the installer prompts you. Note: You can install multiple Python stacks/versions, but only one is active at any given time. Get an IDE (optional) If you come from MATLAB, it s helpful to realize that MATLAB is both a language and an Integrated Development Environment (IDE). Most languages don t have an official IDE, and some people may tell you IDEs are a crutch. If being a cyborg with superpowers is a crutch, then call me a cripple because I find them extremely useful. As with all power, it must be wielded carefully though. The option of getting an IDE is completely up to you. If you want one, here are some options: PyCharm - A fully-featured, rich IDE, it s arguably king of the heavyweights and free. At least try it. Here s the PyCharm quick start guide. Spyder - A MATLAB-like IDE with similar layout, preferred by many working in the scientific realm. Note: Spyder is part of the Anaconda distribution. Here are the Spyder docs. 8 Chapter 2. Installation

CHAPTER 3 Getting Started Getting started with pylinac is easy! Once installed, you can write your own script in a matter of minutes. Each module of pylinac addresses the topic of its name (e.g. the Starshot class, surprisingly, performs starshot analysis). Furthermore, each module is designed as similarly as possible to one another. So once you start using one module, it s easy to use another (see Module Design). Each module also has its own demo to show off what it can do. 3.1 Running a Demo Let s get started by running a demo of the Starshot module. First, import the Starshot class: from pylinac import Starshot This class has all the capabilities of loading and analyzing a Starshot image. Let s 1) create an instance of that class and then 2) run its demonstration method: mystar = Starshot() mystar.run_demo() Running this should result in a printing of information to the console and an image showing the analyzed image, like so: Result: PASS The minimum circle that touches all the star lines has a diameter of 0.434 mm. The center of the minimum circle is at 1270.1, 1437.1 9

Congratulations! In 3 lines you ve successfully used a pylinac module. Of course there s more to it than that; you ll want to analyze your own images. For further documentation on starshots, see Starshot module documentation. 3.2 Loading in Images/Data All modules have multiple ways of loading in your data. The best way to use a given module s main class is instantiating with the image/data file name. If you have something else (e.g. a URL or set of multiple images) you can use the class-based constructors that always start with from_. Let s use the log_analyzer module to demonstrate: from pylinac import MachineLog We can pass the path to the log, and this would be the standard way of constructing: log = MachineLog(r"C:/John/QA/log.dlg") But perhaps we don t know the full path to the file and we want to look around for it: log = MachineLog.from_UI() From here, a dialog box will pop open and you can look around and find your log. Perhaps the data is stored online somewhere. You can load in the data from a URL: log = MachineLog.from_url('https://myserver.com/logs/log23.bin') If for any reason you don t have data and want to experiment, you can easily load in demo data: tlog = MachineLog.from_demo_trajectorylog() dlog = MachineLog.from_demo_dynalog() You can find out more about logs in the Log Analyzer module documentation. All modules are similar however; the main class can be instantiated directly, through class-based constructors, through a UI dialog box, from a URL, and all main classes have a demo dataset and demo method. 10 Chapter 3. Getting Started

CHAPTER 4 Starshot module documentation 4.1 Overview The Starshot module analyses a starshot image made of radiation spokes, whether gantry, collimator, MLC or couch. It is based on ideas from Depuydt et al and Gonzalez et al and evolutionary optimization. Features: 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. 4.2 Running the Demo To run the Starshot demo, create a script or start an interpreter and input: from pylinac import Starshot Starshot().run_demo() Results will be printed to the console and a matplotlib figure showing the analyzed starshot image will pop up: Result: PASS The minimum circle that touches all the star lines has a diameter of 0.434 mm. The center of the minimum circle is at 1270.1, 1437.1 11

4.3 Image Acquisition To capture starshot images, film is often used, but a sequence of EPID images can also work for collimator measurements. Pylinac can automatically superimpose the images. See the literature mentioned in the Overview for more info on acquisition. 4.4 Typical Use The Starshot analysis can be run first by importing the Starshot class: from pylinac import Starshot A typical analysis sequence looks like so: Load image(s) Loading film or superimposed EPID DICOM images can be done by passing the file path or by using a UI to find and get the file. The code might look like any of the following: star_img = "C:/QA Folder/gantry_starshot.tif" mystar = Starshot(star_img) Or, use a dialog box: mystar = Starshot.from_image_UI() Multiple images can be easily superimposed and used; e.g. collimator shots at various angles: mystar = Starshot.from_multiple_images_UI() Or, pass them programatically: star_imgs = ['path/star0.tif', 'path/star45.tif', 'path/star90.tif'] mystar = Starshot.from_multiple_images(star_imgs) 12 Chapter 4. Starshot module documentation

Note: In previous versions of pylinac, loading images was instance-method based. This behavior has been simplified in favor of initialization normalization and adding class-method constructors (Starshot.from_X). The reason for this is that certain actions should only be allowed until after the image is loaded. Furthermore, loading the image should always be the first action of the analysis sequence. By using class constructors, certain pitfalls and errors can be avoided. Don t worry though, the old behavior still works. Analyze the image After loading the image, all that needs to be done is analyze the image. You may optionally pass in some settings: mystar.analyze(radius=50, tolerance=0.8) # see API docs for more parameter info View the results Starshot can print out the summary of results to the console as well as draw a matplotlib image to show the detected radiation lines and wobble circle: # print results to the console print(mystar.return_results()) # view analyzed image mystar.plot_analyzed_image() Each subplot can be plotted independently as well: # just the wobble plot mystar.plot_analyzed_subimage('wobble') # just the zoomed-out plot mystar.plot_analyzed_subimage('whole') Saving the images is also just as easy: mystar.save_analyzed_image('mystar.png') 4.5 Algorithm Allowances The image can be either inversion (radiation is darker or brighter). The image can be any size. The image can be DICOM (from an EPID) or most image formats (scanned film). If multiple images are used, they must all be the same size. Restrictions Warning: Analysis can fail or give unreliable results if any Restriction is violated. The image must have at least 6 spokes (3 angles). The center of the star must be in the central 1/3 of the image. The radiation spokes must extend to both sides of the center. I.e. the spokes must not end at the center of the circle. Pre-Analysis Check for image noise The image is checked for unreasonable noise by comparing the min and max to the 1/99th percentile pixel values respectively. If there is a large difference then there is likely an artifact and a median filter is applied until the min/max and 1/99th percentiles are similar. 4.5. Algorithm 13

Check image inversion The image is checked for proper inversion by summing the image along each axis and then effectively finding the point of maximum value. If the point is not in the central 1/3 of the image, it is thought to be inverted. This check can be skipped but is enabled by default. Set algorithm starting point Unless the user has manually set the pixel location of the start point, it is automatically found by summing the image along each axis and finding the center of the full-width, 80%-max of each sum. The maximum value point is also located. Of the two points, the one closest to the center of the image is chosen as the starting point. Analysis Extract circle profile A circular profile is extracted from the image centered around the starting point and at the radius given. Find spokes The circle profile is analyzed for peaks. Optionally, the profile is reanalyzed to find the center of the FWHM. An even number of spokes must be found (1 for each side; e.g. 3 collimator angles should produce 6 spokes, one for each side of the CAX). Match peaks Peaks are matched to their counterparts opposite the CAX to compose a line using a simple peak number offset. Find wobble Starting at the initial starting point, an evolutionary gradient method is utilized to find the point of minimum distance to all lines. If recursive is set to True and a reasonable wobble (<2mm) is not found using the passes settings, the peak height and radius are iterated until a reasonable wobble is found. Post-Analysis Check if passed Once the wobble is calculated, it is tested against the tolerance given, and passes if below the tolerance. If the image carried a pixel/mm conversion ratio, the tolerance and result are in mm, otherwise they will be in pixels. 4.6 Troubleshooting First, check the general Troubleshooting section, especially if an image won t load. Specific to the starshot analysis, there are a few things you can do. Set recursive to True - This easy step in analyze() allows pylinac to search for a reasonable wobble even if the conditions you passed don t for some reason give one. Make sure the center of the star is in the central 1/3 of the image - Otherwise, pylinac won t find it. Make sure there aren t egregious artifacts - Pin pricks can cause wild pixel values; crop them out if possible. 4.7 API Documentation class pylinac.starshot.starshot(filepath=none, **kwargs) Bases: object Class that can determine the wobble in a starshot image, be it gantry, collimator, couch or MLC. The image can be a scanned film (TIF, JPG, etc) or a sequence of EPID DICOM images. image Image circle_profile StarProfile 14 Chapter 4. Starshot module documentation

lines LineManager wobble Wobble tolerance Tolerance Examples Run the demo: >>> Starshot().run_demo() Typical session: >>> img_path = r"c:/qa/starshots/coll.jpeg" >>> mystar = Starshot(img_path) >>> mystar.analyze() >>> print(mystar.return_results()) >>> mystar.plot_analyzed_image() filepath (str, optional) The path to the image file. If None, the image must be loaded later. kwargs Passed to load(). classmethod from_url(url, **kwargs) Instantiate from a URL. New in version 0.7.1. load_url(url, **kwargs) Load from a URL. url (str) URL of the raw file. kwargs Passed to load(). New in version 0.7.1. url (str) URL of the raw file. kwargs Passed to load(). classmethod from_demo_image() Construct a Starshot instance and load the demo image. New in version 0.6. load_demo_image() Load the starshot demo image. load_image(filepath, **kwargs) Load the image via the file path. 4.7. API Documentation 15

filepath (str) Path to the file to be loaded. kwargs Passed to load(). classmethod from_multiple_images(filepath_list, **kwargs) Construct a Starshot instance and load in and combine multiple images. New in version 0.6. filepath_list (iterable) An iterable of file paths to starshot images that are to be superimposed. kwargs Passed to load(). load_multiple_images(filepath_list, **kwargs) Load multiple images via the file path. New in version 0.5.1. filepath_list (sequence) An iterable sequence of filepath locations. kwargs Passed to load_multiples() analyze(radius=0.85, min_peak_height=0.25, tolerance=1.0, SID=100, start_point=none, fwhm=true, recursive=true) Analyze the starshot image. Analyze finds the minimum radius and center of a circle that touches all the lines (i.e. the wobble circle diameter and wobble center). radius (float, optional) Distance in % between starting point and closest image edge; used to build the circular profile which finds the radiation lines. Must be between 0.05 and 0.95. min_peak_height (float, optional) The percentage minimum height a peak must be to be considered a valid peak. A lower value catches radiation peaks that vary in magnitude (e.g. different MU delivered or gantry shot), but could also pick up noise. If necessary, lower value for gantry shots and increase for noisy images. tolerance (int, float, optional) The tolerance to test against for a pass/fail result. If the image has a pixel/mm conversion factor, the tolerance is in mm. If the image has not conversion factor, the tolerance is in pixels. SID (int, float, optional) The source-to-image distance in cm. If a value!= 100 is passed in, results will be scaled to 100cm. E.g. a wobble of 3.0 pixels at an SID of 150cm will calculate to 2.0 pixels [3 / (150/100)]. Note: For EPID images (e.g. superimposed collimator shots), the SID is in the DICOM file and this value will always be used if it can be found, otherwise the passed value will be used. start_point (2-element iterable, optional) A point where the algorithm should use for determining the circle profile. If None (default), will search for a reasonable maximum point nearest the center of the image. 16 Chapter 4. Starshot module documentation

fwhm (bool) If True (default), the center of the FWHM of the spokes will be determined. If False, the peak value location is used as the spoke center. Note: In practice, this ends up being a very small difference. Set to false if peak locations are offset or unexpected. recursive (bool) If True (default), will recursively search for a reasonable wobble, meaning the wobble radius is <3mm. If the wobble found was unreasonable, the minimum peak height is iteratively adjusted from low to high at the passed radius. If for all peak heights at the given radius the wobble is still unreasonable, the radius is then iterated over from most distant inward, iterating over minimum peak heights at each radius. If False, will simply return the first determined value or raise error if a reasonable wobble could not be determined. Warning: It is strongly recommended to leave this setting at True. Raises AttributeError If an image has not yet been loaded. RuntimeError If a reasonable wobble value was not found. image_is_loaded Boolean property specifying if an image has been loaded. passed Boolean specifying whether the determined wobble was within tolerance. return_results() Return the results of the analysis. Returns A string with a statement of the minimum circle. Return type string plot_analyzed_image(show=true) Draw the star lines, profile circle, and wobble circle on a matplotlib figure. show (bool) Whether to actually show the image. plot_analyzed_subimage(subimage= wobble, ax=none, show=true) Plot a subimage of the starshot analysis. Current options are the zoomed out image and the zoomed in image. subimage (str) If wobble, will show a zoomed in plot of the wobble circle. Any other string will show the zoomed out plot. ax (None, matplotlib Axes) If None (default), will create a new figure to plot on, otherwise plot to the passed axes. save_analyzed_image(filename, **kwargs) Save the analyzed image plot to a file. filename (str, IO stream) The filename to save as. Format is deduced from string extention, if there is one. E.g. mystar.png will produce a PNG image. kwargs All other kwargs are passed to plt.savefig(). 4.7. API Documentation 17

save_analyzed_subimage(filename, subimage= wobble, **kwargs) Save the analyzed subimage to a file. filename (str, file-object) Where to save the file to. subimage (str) If wobble, will show a zoomed in plot of the wobble circle. Any other string will show the zoomed out plot. kwargs Passed to matplotlib. static run_demo() Demonstrate the Starshot module using the demo image. class pylinac.starshot.starprofile(image, start_point, radius, min_peak_height, fwhm) Bases: pylinac.core.profile.collapsedcircleprofile Class that holds and analyzes the circular profile which finds the radiation lines. get_peaks(min_peak_height, min_peak_distance=0.02, fwhm=true) Determine the peaks of the profile. class pylinac.starshot.wobble(center_point=none, radius=none) Bases: pylinac.core.geometry.circle A class that holds the wobble information of the Starshot analysis. radius_mm The radius of the Circle in **mm*.* class pylinac.starshot.linemanager(points) Bases: object Manages the radiation lines found. points The peak points found by the StarProfile construct_rad_lines(points) Find and match the positions of peaks in the circle profile (radiation lines) and map their positions to the starshot image. Radiation lines are found by finding the FWHM of the radiation spokes, then matching them to form lines. See also: Returns lines A list of Lines (radiation lines) found. Return type list Starshot.analyze() core.profile.circleprofile.find_fwxm_peaks() min_peak_distance parameter info. geometry.line() returning object match_points(points) Match the peaks found to the same radiation lines. Peaks are matched by connecting the existing peaks based on an offset of peaks. E.g. if there are 12 peaks, there must be 6 radiation lines. Furthermore, assuming star lines go all the way across the CAX, the 7th peak will be the opposite peak of the 1st peak, forming a line. This method is robust to starting points far away from the real center. plot(axis) Plot the lines to the axis. 18 Chapter 4. Starshot module documentation

class pylinac.starshot.tolerance(value=none, unit=none) Bases: object A class for holding tolerance information. 4.7. API Documentation 19

20 Chapter 4. Starshot module documentation

CHAPTER 5 VMAT module documentation 5.1 Overview The VMAT module consists of the class VMAT, which is 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 Dose-Rate & MLC speed (DRMLC) tests. Features: Do both tests - Pylinac can handle either DRGS or DRMLC tests. Adjust for offsets - Older VMAT patterns were off-center. Easily account for the offset by passing it in. Automatic identification using file names - If your file names are clear, the image type and test type don t even have to be specified; just load and analyze. 5.2 Running the Demo To run the VMAT demo, create a script or start an interpreter session and input: from pylinac import VMAT VMAT().run_demo_drgs() # alternatively, you can run the MLC Speed demo by: VMAT().run_demo_mlcs() Results will be printed to the console and a figure showing both the Open field and MLC field image will pop up: Dose Rate & MLC Speed Test Results (Tol. +/-1.5%): PASS Max Deviation: 0.437% Absolute Mean Deviation: 0.382% 21

5.3 Image Acquisition If you want to perform these specific QA tests, you ll need DICOM plan files that control the linac precisely to deliver the test fields. These can be downloaded from my.varian.com. Once logged in, search for RapidArc and you should see two items called RapidArc QA Test Procedures and Files for TrueBeam ; there will be a corresponding one for C-Series. Use the RT Plan files and follow the instructions, not including the assessment procedure, which is the point of this module. Save & move the images to a place you can use pylinac. 5.4 Naming Convention For VMAT analysis, pylinac needs to know which image is the DMLC image. You can specify this when loading an image and pass the test type directly. However, if you follow a simple naming convention, you can skip that step. If the file has open or dmlc (case-insenitive) in the file name, pylinac will automatically assign the images. Also, if the filenames have drmlc or drgs in the file names, then the test type is automatically registered as well. The following example shows what this would look like: # well-named files img1 = r'c:/path/drmlc_open.dcm' img2 = r'c:/path/drmlc_dmlc.dcm' # reading these in registers both image type and test type vmat = VMAT((img1, img2)) vmat.analyze() # no need for test type If your files don t follow this convention it s okay; you just need to specify the image and test types manually. See below and analyze() for more info. 5.5 Typical Use The VMAT QA analysis follows what is specified in the Varian RapidArc QA tests and assumes your tests will run the exact same way. Import the class: from pylinac import VMAT The minimum needed to get going is to: Load images Loading the EPID DICOM images into your VMAT class object can be done by passing the file path(s) or by using a UI to find and get the file(s). While each image can be loaded directly, using an easy Naming Convention can simplify the process: 22 Chapter 5. VMAT module documentation

open_img = "C:/QA Folder/VMAT/open_field.dcm" # note the 'open' dmlc_img = "C:/QA Folder/VMAT/dmlc_field.dcm" # no 'open' myvmat = VMAT((open_img, dmlc_img)) # the order doesn't matter Even easier, you can load the files using a UI dialog box: myvmat = VMAT.from_images_UI() # a dialog box will pop up for you to choose both images You can also load the images one at a time. This is helpful when the file names do not follow the naming convention: open_img = "path/to/img1.dcm" dmlc_img = "path/to/img2.dcm" myvmat = VMAT() myvmat.load_image(open_img, im_type='open') myvmat.load_image(dmlc_img, im_type='dmlc') Or load the files individually from a UI dialog box: myvmat = VMAT() myvmat.load_image_ui(im_type='open') myvmat.load_image_ui(im_type='mlc') # tkinter UI box will pop up Finally, a zip file holding both the images can be used, but must follow the Naming Convention: myvmat = VMAT.from_zip(r'C:/path/to/zip.zip') Note: In previous versions of pylinac, loading images was instance-method based and only allowed one image at a time, meaning loading looked like the 3rd example above, no matter the name. This behavior has been simplified in favor of initialization normalization and adding class-method constructors (VMAT.from_X). The reason for this is that certain actions should only be allowed until after the images are loaded. Furthermore, loading the images should always be the first action of the analysis sequence. By using class constructors, certain pitfalls and errors can be avoided. Don t worry though, the old behavior still works. Analyze the images This is where pylinac does its work. Once the images are loaded, tell VMAT to analyze the images. See the Algorithm section for details on how this is done. Tolerance can also be passed, but has a default value of 1.5%: myvmat.analyze(test='drmlc', tolerance=1.5) Note: The test parameter does not need to be specified if the Naming Convention for tests is followed. View the results The VMAT module can print out the summary of results to the console as well as draw a matplotlib image to show where the segments were placed and their values: # print results to the console print(myvmat.return_results()) # view analyzed images myvmat.plot_analyzed_image() 5.5. Typical Use 23

The individual plots can also be plotted and saved: myvmat.plot_analyzed_subimage('profile') myvmat.save_analyzed_subimage('myprofile.png', subimage='profile') 5.6 Tips & Tricks The VMAT test can be customized using the tolerance parameter to whatever tolerance is needed, whether simply using a default (1.5), or a clinical value. Furthermore, the location of the segments can be adjusted. Older QA tests had the segments slightly offset from center. The new revision of tests is now centered. To account for this, just add an offset to analyze: myvmat = VMAT.from_demo_images('drgs') myvmat.analyze(x_offset=20) The test results are also not constricted to just printing out. Important results can be accessed directly. Continuing from the example above: myvmat.avg_abs_r_deviation 0.357 myvmat.max_r_deviation # regardless of sign -1.423 myvmat.passed True 5.7 Algorithm The VMAT analysis algorithm is based on the Varian RapidArc QA Test Procedures for C-Series and Truebeam. Two tests (besides Picket Fence, which has its own module) are specified. Each test takes 10x0.5cm samples, each corresponding to a distinct section of radiation. A corrected reading of each segment is made, defined as: R corr (x) = R DRGS (x) R open(x) * 100. The reading deviation of each segment is calculated as: R deviation(x) = Rcorr(x) R corr R corr is the average of all segments. The algorithm works like such: Allowances The images can be acquired at any SID. The images can be acquired with any EPID (as500, as1000, as1200). Restrictions * 100 100, where 24 Chapter 5. VMAT module documentation

Warning: Analysis can fail or give unreliable results if any Restriction is violated. The tests must be delivered using the DICOM RT plan files provided by Varian which follow the test layout of Ling et al. The images must be acquired with the EPID. Pre-Analysis Determine image scaling Segment determination is based on offsets from the center pixel of the image. However, some physicists use 150 cm SID and others use 100 cm, and others can use a clinical setting that may be different than either of those. To account for this, the SID is determined and then scaling factors are determined to be able to perform properly-sized segment analysis. Analysis Note: Calculations tend to be lazy, computed only on demand. This represents a nominal analysis where all calculations are performed. Calculate sample boundaries Segment positions are always the same and are determined by offsets from the center pixel given by Varian. These values are then scaled with the image scaling factor determined above. Calculate the corrected reading For each segment, the mean pixel value is determined for both the open and DMLC image. These values are used to determine the corrected reading: R corr. Calculate sample and segment ratios Once the images are normalized to themselves, the sample values of the DMLC field are divided by their corresponding open field values. Calculate segment deviations Segment deviation is then calculated once all the corrected readings are determined. The average absolute deviation can also be calculated. Post-Analysis Test if segments pass tolerance Each segment is checked to see if it was within the specified tolerance. If any samples fail, the whole test is considered failing. 5.8 API Documentation class pylinac.vmat.vmat(images=none) Bases: object The VMAT class analyzes two DICOM images acquired via a linac s EPID and analyzes regions of interest (segments) based on the Varian RapidArc QA specifications, specifically, the Dose Rate & Gantry Speed (DRGS) and Dose Rate & MLC speed (DRMLC) tests. image_open Image image_dmlc Image segments SegmentManager settings Settings 5.8. API Documentation 25

Examples Run the DRGS demo: >>> VMAT().run_demo_drgs() Run the DRMLC demo: >>> VMAT().run_demo_drmlc() A typical use case: >>> open_img = "C:/QA Folder/VMAT/open_field.dcm" >>> dmlc_img = "C:/QA Folder/VMAT/dmlc_field.dcm" >>> myvmat = VMAT((open_img, dmlc_img)) >>> myvmat.analyze(test='drmlc', tolerance=1.5) >>> print(myvmat.return_results()) >>> myvmat.plot_analyzed_image() load_image(file_path, im_type=none) Load the image directly by the file path. file_path (str, file-like object) The path to the DICOM image or I/O stream. im_type ({ open, mlcs, None}) Specifies what image type is being loaded in. If None, will try to determine the type from the name. The name must have open or dmlc in the name. load_images(images, names=none) Load both images simultaneously, assuming a clear name convention: 1) The open image must have open in the filename. 2) Optionally, the test type can be in the image names as well. If so, the test type is automatically determined. New in version 0.6. images (str) File paths to the images. Order does not matter. The open image must have open somewhere in the name. names (None, str) Internal keyword. If the passed images are URLs, then the names must also be passed. If loading URLs, use.from_urls() instead. classmethod from_demo_images(type= drgs ) Construct a VMAT instance using the demo images. New in version 0.6. load_demo_image(type= drgs ) Load the demo DICOM images from demo files folder. type ({ drgs, drmlc }) Test type of images to load. classmethod from_zip(path) Load VMAT images from a ZIP file that contains both images. New in version 0.8. 26 Chapter 5. VMAT module documentation

load_zip(zip_file) Load VMAT images from a ZIP file that contains both images. New in version 0.8. classmethod from_urls(urls) Load from URLs. New in version 0.7.1. load_urls(urls) Load from URLs. New in version 0.7.1. static run_demo_drgs(tolerance=1.5) Run the VMAT demo for the Dose Rate & Gantry Speed test. static run_demo_drmlc(tolerance=1.5) Run the VMAT demo for the MLC leaf speed test. analyze(test=none, tolerance=1.5, x_offset=0) Analyze the open and DMLC field VMAT images, according to 1 of 2 possible tests. test ({ drgs, mlcs, None}) The test to perform, either Dose Rate Gantry Speed ( drgs ) or MLC Speed ( mlcs ). The default is None, which assumes a clear naming convention; see load_image() for info. tolerance (float, int, optional) The tolerance of the sample deviations in percent. Default is 1.5. Must be between 0 and 8. x_offset (int) New in version 0.8. If the VMAT segments aren t aligned to the CAX (older VMAT images), the segments need a shift. Positive moves the segments right, negative to the left. avg_abs_r_deviation Return the average of the absolute R_deviation values. avg_r_deviation Return the average of the R_deviation values, including the sign. max_r_deviation Return the value of the maximum R_deviation segment. open_img_is_loaded Status of open image. dmlc_img_is_loaded Status of DMLC image. passed Returns whether all the segment ratios passed the given tolerance. plot_analyzed_image(show=true) Plot the analyzed images. Shows the open and dmlc images with the segments drawn; also plots the median profiles of the two images for visual comparison. show (bool) Whether to actually show the image. save_analyzed_image(filename, **kwargs) Save the analyzed images as a png file. 5.8. API Documentation 27

filename (str, file-object) Where to save the file to. kwargs Passed to matplotlib. plot_analyzed_subimage(subimage= dmlc, show=true, ax=none) Plot an individual piece of the VMAT analysis. subimage (str) Specifies which image to plot. show (bool) Whether to actually plot the image. ax (matplotlib Axes, None) If None (default), creates a new figure to plot to, otherwise plots to the given axes. save_analyzed_subimage(filename, subimage= dmlc, interactive=false, **kwargs) Save a subplot to file. filename (str, file-object) Where to save the file to. subimage (str) Which subplot to save. interactive (bool) Only applicable for the profile subplot. If False, saves as a.png image, else saves as an interactive HTML file. kwargs Passed to matplotlib. median_profiles Return two median profiles from the open and dmlc image. For visual comparison. return_results() A string of the summary of the analysis results. Returns The results string showing the overall result and deviation statistics by segment. Return type str class pylinac.vmat.settings(test_type, tolerance) Bases: object A helper class to hold analysis settings. test_type str The test type being done; either drgs or mlcs tolerance float The tolerance value in percentage; e.g. 0.03 == 3% x_offset int Offset in pixels to apply to all segments. y_offset int Offset in pixels to apply to all segments. 28 Chapter 5. VMAT module documentation

class pylinac.vmat.segmentmanager(open_img, dmlc_img, settings) Bases: object A class to handle the VMAT segments and perform actions across all segments. draw(plot) Draw the segments onto a plot. plot (matplotlib.axes.axes) The plot to draw the objects on. r_devs Return the deviations of all segments as an array. avg_abs_r_deviation Return the average of the absolute R_deviation values. avg_r_deviation Return the average of the R_deviation values, including the sign. max_r_deviation Return the value of the maximum R_deviation segment. passed Return whether all the images passed. class pylinac.vmat.segment(center_point, open_image, dmlc_image, tolerance) Bases: pylinac.core.geometry.rectangle A class for holding and analyzing segment data of VMAT tests. For VMAT tests, there are either 4 or 7 segments, which represents a section of the image that received radiation under the same conditions. r_dev float r_corr float The reading deviation (R_dev) from the average readings of all the segments. See RTD for equation info. The corrected reading (R_corr) of the pixel values. See RTD for explanation and equation info. passed boolean Specifies where the segment reading deviation was under tolerance. r_corr Return the ratio of the mean pixel values of DMLC/OPEN images. passed Return whether the segment passed or failed. get_bg_color() Get the background color of the segment when plotted, based on the pass/fail status. 5.8. API Documentation 29

30 Chapter 5. VMAT module documentation

CHAPTER 6 CBCT module documentation 6.1 Overview The CBCT module automatically analyzes DICOM images of a CatPhan 504 or 503 acquired when doing CBCT or regular CT quality assurance. It can load a folder or zip file that the images are in and automatically correct for phantom setup in 6 degrees. It can analyze the HU regions and image scaling (CTP404), the high-contrast line pairs (CTP528) to calculate the modulation transfer function (MTF), the HU uniformity (CTP486), and Low Contrast (CTP515) on the corresponding slices. As of version 1.2, the cbct module can analyze the CatPhan 503, Elekta s CBCT phantom. Features: 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; even scan it in a regular CT scanner. Any field size or field extent is allowed. 6.2 Running the Demo To run the CBCT demo, create a script or start in interpreter and input: from pylinac import CBCT CBCT().run_demo() # the demo is a Varian high quality head scan Results will be printed to the console and a figure showing the slices analyzed will pop up: - CBCT QA Test - HU Regions: {'LDPE': -102.66666666666667, 'Acrylic': 117.33333333333333, 'Air': -998.66666666666663, HU Passed?: True Uniformity: {'Right': 0.0, 'Top': 4.666666666666667, 'Left': 10.666666666666666, 'Center': 16.3333333 Uniformity Passed?: True MTF 80% (lp/mm): 0.76 Geometric Line Average (mm): 49.93901922841176 Geometry Passed?: True Low Contrast ROIs visible: 4 Low Contrast Passed? True Slice Thickness (mm): 2.4705078124999997 Slice Thickeness Passed? True 31

As well, you can plot and save individual pieces of the analysis: cbct = CBCT.from_demo_images() cbct.analyze() cbct.plot_analyzed_subimage('linearity') cbct.save_analyzed_subimage('linearity.png', subimage='linearity') Or: cbct.plot_analyzed_subimage('rmtf') 6.3 Typical Use CBCT analysis as done by this module closely follows what is specified in the CatPhan manuals, replacing the need for manual measurements. First, import the class: from pylinac import CBCT The minimum needed to get going is to: Load images Loading the DICOM images into your CBCT object can be done by passing the folder the images are located in. This can be done directly, or by using a UI. The code might look like any of the following: # set the folder path cbct_folder = r"c:/qa Folder/CBCT/June monthly" # load the images from the folder path mycbct = CBCT(cbct_folder) # use of 'r' is for raw string; otherwise space or load a zip file of the images: zip_file = r"c:/qa Folder/CBCT/June monthly.zip" mycbct = CBCT.from_zip_file(zip_file) or using a dialog box: 32 Chapter 6. CBCT module documentation

# Identify the folder using a UI mycbct = CBCT.from_folder_UI() Note: In previous versions of pylinac, loading images was instance-method based, meaning loading looked like the following: mycbct = CBCT() mycbct.load_zip_file('cbcts.zip') This behavior has been deprecated in favor of class-method constructors (CBCT.from_X). The reason for this is that certain actions should only be allowed until after the images are loaded. Furthermore, loading the images should always be the first action of the analysis sequence. By using class constructors, certain pitfalls and errors can be avoided. Don t worry though, the old behavior still works. Analyze the images Once the folder/images are loaded, tell CBCT to start analyzing the images. See the Algorithm section for details on how this is done: mycbct.analyze() View the results The CBCT module can print out the summary of results to the console as well as draw a matplotlib image to show where the samples were taken and their values: # print results to the console print(mycbct.return_results()) # view analyzed images mycbct.plot_analyzed_image() # save the image mycbct.save_analyzed_image('mycbct.png') 6.4 Algorithm The CBCT module is based on the tests and values given in the CatPhan 504 Manual. The algorithm works like such: Allowances The images can be any size. The phantom can have significant translation in all 3 directions. The phantom can have significant roll and moderate yaw and pitch. Restrictions Warning: Analysis can fail or give unreliable results if any Restriction is violated. The phantom used must be an unmodified CatPhan 504. All 4 of the relevant modules must be within the scan extent; i.e. one can t scan only part of the phantom. Pre-Analysis Determine image properties Upon load, the image set is analyzed for its DICOM properties to determine mm/pixel spacing, rescale intercept and slope, manufacturer, etc. Convert to HU The entire image set is converted from its raw values to HU by applying the rescale intercept and slope which is contained in the DICOM properties. 6.4. Algorithm 33

Find the phantom z-location Upon loading, all the images are scanned to determine where the HU linearity module (CTP404) is located. This is accomplished by examining each image slice and looking for 2 things: Analysis If the CatPhan is in the image. At the edges of the scan this may not be true. If a circular profile has characteristics like the CTP404 module. If the CatPhan is in the image, a circular profile is taken at the location where the HU linearity regions of interest are located. If the profile contains low, high, and lots of medium values then it is very likely the HU linearity module. All such slices are found and the median slice is set as the HU linearity module location. All other modules are located relative to this position. Determine phantom roll Precise knowledge of the ROIs to analyze is important, and small changes in rotation could invalidate automatic results. The roll of the phantom is determined by examining the HU module and converting to a binary image. The air holes are then located and the angle of the two holes determines the phantom roll. Note: For each step below, the module analyzed is actually the mean, median, or maximum of 3 slices (+/-1 slice around and including the nominal slice) to ensure robust measurements. Also, for each step/phantom module, the phantom center is determined, which corrects for the phantom pitch and yaw. Additionally, values tend to be lazy (computed only when asked for), thus the calculations listed may sometimes be performed only when asked for. Determine HU linearity The HU module (CTP404) contains several materials with different HU values. Using hardcoded angles (corrected for roll) and radius from the center of the phantom, circular ROIs are sampled which correspond to the HU material regions. The mean pixel value of the ROI is the stated HU value. Determine HU uniformity HU uniformity (CTP486) is calculated in a similar manner to HU linearity, but within the CTP486 module/slice. Calculate Geometry/Scaling The HU module (CTP404), besides HU materials, also contains several nodes which have an accurate spacing (50 mm apart). Again, using hardcoded but corrected angles, the area around the 4 nodes are sampled and then a threshold is applied which identifies the node within the ROI sample. The center of mass of the node is determined and then the space between nodes is calculated. Calculate Spatial Resolution/MTF The Spatial Resolution module (CTP528) contains 21 pairs of aluminum bars having varying thickness, which also corresponds to the thickness between the bars. One unique advantage of these bars is that they are all focused on and equally distant to the phantom center. This is taken advantage of by extracting a CollapsedCircleProfile about the line pairs. The peaks and valleys of the profile are located; peaks and valleys of each line pair are used to calculated the MTF. The relative MTF (i.e. normalized to the first line pair) is then calculated from these values. Calculate Low Contrast Resolution Low contrast is inherently difficult to determine since detectability of humans is not simply contrast based. Pylinac s analysis uses both the contrast value of the ROI as well as the ROI size to compute a detectability score. ROIs above the score are said to be seen, while those below are not seen. Only the 1.0% supra-slice ROIs are examined. Two background ROIs are sampled on either side of the ROI contrast set. The score for a given ROI is calculated like so ROI pixel background ROI stdev * ROI diameter, where ROI pixel is the mean pixel value of the ROI, background is the mean pixel value of the two background ROIs, and ROI diameter is the diamter of the ROI in mm. The default detectability score is 10. Calculate Slice Thickness Slice thickness is measured by determining the FWHM of the wire ramps in the CTP404 module. A profile of the area around each wire ramp is taken, and the FWHM is determined from the profile. Based on testing, the FWHM is not always perfectly detected and may not catch the profile, giving an undervalued representation. Thus, the two longest profiles are averaged and the value is converted from pixels to mm and multiplied by 0.42. 34 Chapter 6. CBCT module documentation

Post-Analysis Test if values are within tolerance For each module, the determined values are compared with the nominal values. If the difference between the two is below the specified tolerance then the module passes. 6.5 Troubleshooting First, check the general Troubleshooting section. Most problems in this module revolve around getting the data loaded. If you re having trouble getting your dataset in, make sure you re loading the whole dataset. Also make sure you ve scanned the whole phantom. Make sure there are no external markers on the CatPhan (e.g. BBs), otherwise the localization algorithm will not be able to properly locate the phantom within the image. 6.6 API Documentation The CBCT class uses several other classes. There are several Slices of Interest (SOI), most of which contain Regions of Interest (ROI). class pylinac.cbct.cbct(folderpath=none) Bases: object A class for loading and analyzing CT DICOM files of a CatPhan 504 & CatPhan 503. Can be from a CBCT or CT scanner Analyzes: Uniformity (CTP486), High-Contrast Spatial Resolution (CTP528), Image Scaling & HU Linearity (CTP404). dicom_stack DICOMStack settings Settings hu HUSlice uniformity UniformitySlice geometry GeometrySlice lowcontrast LowContrastSlice spatialres SpatialResolutionSlice thickness ThicknessSlice Examples Run the demo: 6.5. Troubleshooting 35

>>> CBCT().run_demo() Typical session: >>> cbct_folder = r"c:/qa/cbct/june" >>> mycbct = CBCT(cbct_folder) >>> mycbct.analyze() >>> print(mycbct.return_results()) >>> mycbct.plot_analyzed_image() folderpath (str, None) If None, the images must be loaded later. If a string, must point to the CBCT image folder location. classmethod from_demo_images() Construct a CBCT object and load the demo images. New in version 0.6. load_demo_images() Load the CBCT demo images. classmethod from_url(url) Instantiate from a URL. load_url(url) Load from a URL. url (str) URL pointing to a zip archive of CBCT images. versionadded (.) New in version 0.7.1. load_folder(folder) Load the CT DICOM files string input. folder (str) Path to the folder. Raises NotADirectoryError : If folder str passed is not a valid directory. FileNotFoundError : If no CT images are found in the folder classmethod from_zip_file(zip_file) Construct a CBCT object and pass the zip file. New in version 0.6. load_zip_file(zip_file) Load a CBCT dataset from a zip file. zip_file (str, ZipFile) Path to the zip file or a ZipFile object. Raises FileExistsError : If zip_file passed was not a legitimate zip file. FileNotFoundError : If no CT images are found in the folder plot_analyzed_image(hu=true, geometry=true, uniformity=true, spatial_res=true, thickness=true, low_contrast=true, show=true) Plot the images used in the calculate and summary data. 36 Chapter 6. CBCT module documentation

hu (bool) Whether to show the HU linearity circles. geometry (bool) Whether to show the geometric lines found. uniformity (bool) Whether to show the uniformity ROIs. spatial_res (bool) Whether to show the spatial resolution circle outline. thickness (bool) Whether to show the wire ramp boxes. low_contrast (bool) Whether to show the low contrast bubble circles. show (bool) Whether to plot the image or not. save_analyzed_image(filename, **kwargs) Save the analyzed summary plot. filename (str, file object) The name of the file to save the image to. kwargs Any valid matplotlib kwargs. plot_analyzed_subimage(subimage= hu, delta=true, interactive=false, show=true) Plot a specific component of the CBCT analysis. subimage ({ hu, un, sp, lc, mtf, lin, prof }) 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. mtf draws the RMTF plot. lin draws the HU linearity values. Used with delta. prof draws the HU uniformity profiles. delta (bool) Only for use with lin. Whether to plot the HU delta or actual values. interactive (bool) If False (default), the figure is plotted statically as a matplotlib figure. If True, the figure is plotted as an HTML page in the default browser that can have tooltips. This setting is only applicable for mtf, lin, and prof, i.e. the regular plots. Image plotting does not work with mpld3. Note: mpld3 must be installed to use this feature. show (bool) Whether to actually show the plot. save_analyzed_subimage(filename, subimage= hu, interactive=false, **kwargs) Save a component image to file. filename (str, file object) The file to write the image to. 6.6. API Documentation 37

subimage (str) See plot_analyzed_subimage() for parameter info. interactive (bool) If False (default), saves a matplotlib figure as a.png image. If True, saves an html file, which can be opened in a browser, etc. Note: mpld3 must be installed to use this feature. return_results() Return the results of the analysis as a string. Use with print(). analyze(hu_tolerance=40, scaling_tolerance=1, thickness_tolerance=0.2, low_contrast_tolerance=1, contrast_threshold=15) Single-method full analysis of CBCT DICOM files. hu_tolerance (int) The HU tolerance value for both HU uniformity and linearity. scaling_tolerance (float, int) The scaling tolerance in mm of the geometric nodes on the HU linearity slice (CTP404 module). thickness_tolerance (float, int) The tolerance of the thickness calculation in mm, based on the wire ramps in the CTP404 module. Warning: Thickness accuracy degrades with image noise; i.e. low mas images are less accurate. low_contrast_tolerance (int) The number of low-contrast bubbles needed to be seen to pass. contrast_threshold (float, int) The threshold for detecting low-contrast image. See RTD for calculation info. static run_demo(show=true) Run the CBCT demo using high-quality head protocol images. images_loaded Boolean property specifying if the images have been loaded. Supporting Data Structure class pylinac.cbct.settings(dicom_stack) Bases: object Data structure for retaining certain settings and information regarding the CBCT algorithm and image data. This class is initialized during the CBCT image loading. threshold int The threshold for converting the image to binary (for things like phantom position locating). Default is -800. air_bubble_radius_mm int, float The size of the Air HU ROIs in mm; for finding the phantom roll. air_bubble_size The size of the Air HU ROIs in pixels; for finding the phantom roll. 38 Chapter 6. CBCT module documentation

manufacturer The manufacturer of the equipment. mm_per_pixel The millimeters per pixel of the DICOM images. hu_slice_num Using a brute force search of the images, find the median HU linearity slice. This method walks through all the images and takes a collapsed circle profile where the HU linearity ROIs are. If the profile contains both low (<800) and high (>800) HU values and most values are the same (i.e. it s not an artifact), then it can be assumed it is an HU linearity slice. The median of all applicable slices is the center of the HU slice. Returns The middle slice of the HU linearity module. Return type int un_slice_num The HU uniformity slice number. sr_slice_num The Spatial Resolution slice number. lc_slice_num The low contrast slice number. phantom_roll Determine the roll of the phantom. This algorithm uses the two air bubbles in the HU slice and the resulting angle between them. Returns float Return type the angle of the phantom in degrees. expected_phantom_size The expected size of the phantom in pixels, based on a 20cm wide phantom. num_images Return the number of images loaded. Slice Objects class pylinac.cbct.slice(dicom_stack, settings, slice_num=none, combine=true, combine_method= mean, num_slices=1) Bases: object Base class for analyzing specific slices of a CBCT dicom set. dicom_stack (DicomImageStack) settings (Settings) slice_num (int) The slice number of the DICOM array desired. If None, will use the slice_num property of subclass. combine (bool) If True, combines the slices +/- num_slices around the slice of interest to improve signal/noise. combine_method ({ mean, max }) How to combine the slices if combine is True. 6.6. API Documentation 39

num_slices (int) The number of slices on either side of the nominal slice to combine to improve signal/noise; only applicable if combine is True. phan_center Determine the location of the center of the phantom. The image is analyzed to see if: 1) the CatPhan is even in the image (if there were any ROIs detected) 2) an ROI is within the size criteria of the catphan 3) the ROI area that is filled compared to the bounding box area is close to that of a circle Raises ValueError If any of the above conditions are not met. class pylinac.cbct.huslice(dicom_stack, settings) Bases: pylinac.cbct.slice, pylinac.cbct.roimanagermixin Class for analysis of the HU linearity slice of the CBCT dicom data set. Although the same CBCT slice is used for other analyses (Geometry, Thickness), this class is only interested in the linearity ROIs. The class analyzes 7 of the ROIs (skipping the 2nd air ROI); these values should be linear. roi_nominal_values list A list of the nominal HU values for the ROIs; in same order as roi_names. slice_num The CBCT slice number for the HU linearity ROIs. tolerance The tolerance of the HU linearity ROIs. plot_linearity(axis=none, plot_delta=true) Plot the HU linearity values to an axis. axis (None, matplotlib.axes) The axis to plot the values on. If None, will create a new figure. plot_delta (bool) Whether to plot the actual measured HU values (False), or the difference from nominal (True). overall_passed Boolean specifying whether all the ROIs passed within tolerance. class pylinac.cbct.uniformityslice(dicom_stack, settings) Bases: pylinac.cbct.slice, pylinac.cbct.roimanagermixin Class for analysis of the Uniformity slice of the CBCT dicom data set. Measures 5 ROIs around the slice that should all be close to the same value. slice_num The CBCT slice number for the HU uniformity ROIs. tolerance The tolerance of the HU uniformity ROIs. plot_profiles(axis=none) Plot the horizontal and vertical profiles of the Uniformity slice. axis (None, matplotlib.axes) The axis to plot on; if None, will create a new figure. overall_passed Boolean specifying whether all the ROIs passed within tolerance. 40 Chapter 6. CBCT module documentation

class pylinac.cbct.geometryslice(dicom_stack, settings) Bases: pylinac.cbct.slice, pylinac.cbct.roimanagermixin Class for analysis of the Geometry slice of the CBCT dicom data set. Four ROIs are set, which correspond to the locations of the 1 air and 3 acrylic nodes. Within these ROIs the center of the nodes must be found. Once the nodes centers are found four lines are constructed by linking the node centers. line_assignments dict lines dict The assignment of line -> nodes. Holds ~pylinac.cbct.geometricline instances. tolerance Tolerance of the geometric lines. slice_num Geometric slice number. plot_lines(axis) Plot the geometric node connection lines. overall_passed Teturns whether all the line lengths were within tolerance. class pylinac.cbct.spatialresolutionslice(*args, **kwargs) Bases: pylinac.cbct.slice Class for analysis of the Spatial Resolution slice of the CBCT dicom data set. A collapsed circle profile is taken of the line-pair region. This profile is search for peaks and valleys. The MTF is calculated from those peaks & valleys. line_pair_frequency tuple The frequency of line pairs for the first 6 regions. num_peaks array The index of the peaks for the various regions. num_valleys array The index of the valleys for the various regions. radius2linepairs_mm float The radius in mm to the line pairs. line_pair_cutoff int The approximate index to stop analyzing the line profile (higher indices are too hard to analyze). 6.6. API Documentation 41

slice_num The slice number of the spatial resolution module. radius2linepairs Radius from the phantom center to the line-pair region, corrected for pixel spacing. plot_rois(axis) Plot the circles where the profile was taken within. circle_profile Calculate the median profile of the Line Pair region. Returns :class: pylinac.core.profile.collapsedcircleprofile Return type A 1D profile of the Line Pair region. spaced_circle_profile The median profile of the line pair region with equalized spacing applied. The line pairs change in spacing, and this function stretches out the later sections to be similar in peak spacing as those of the larger areas. profile_peaks_idxs Return the peak values and indices of the spaced-out profile. profile_peaks_vals Return the peak values and indices of the spaced-out profile. profile_valleys_idxs Return the valley values and indices of the spaced-out profile, with valleys in-between line-pair regions removed. profile_valleys_vals Return the valley values and indices of the spaced-out profile, with valleys in-between line-pair regions removed. mtf(percent=none, region=none) Return the MTF value of the spatial resolution. Only one of the two parameters may be used. percent (int, float) The percent relative MTF; i.e. 0-100. region (int) The line-pair region desired (1-6). Returns float Return type the line-pair resolution at the given MTF percent or region. line_pair_mtfs The discrete MTF values at the line-pair regions. plot_mtf(axis=none) Plot the Relative MTF. axis (None, matplotlib.axes) The axis to plot the MTF on. If None, will create a new figure. class pylinac.cbct.lowcontrastslice(dicom_stack, settings) Bases: pylinac.cbct.slice, pylinac.cbct.roimanagermixin Class for analysis of the low contrast slice of the CBCT dicom data set. Low contrast is measured by obtaining the average pixel value of the contrast ROIs and comparing that value to the average background value. To obtain a more human detection level, the contrast (which is largely the same across different-sized ROIs) is multiplied by the diameter. This value is compared to the contrast threshold to decide if it can be seen. 42 Chapter 6. CBCT module documentation

roi_background_names list A list identifying which of the roi_names are the background ROIs. bg_roi_radius A list of the ROI radii, scaled to pixels. roi_radius A list of the ROI radii, scaled to pixels. rois_visible The number of ROIs visible. plot_rois(axis) Plot the ROIs to an axis. overall_passed Whether there were enough low contrast ROIs seen. plot_contrast(axis=none) Plot the contrast constant. axis (None, matplotlib.axes) The axis to plot the contrast on. If None, will create a new figure. class pylinac.cbct.thicknessslice(dicom_stack, settings) Bases: pylinac.cbct.slice, pylinac.cbct.roimanagermixin ROI Objects This class analyzes the angled wire on the HU slice to determine the slice thickness. roi_widths_mm list The widths of the rectangular ROIs in mm. Follows the order of roi_names. roi_heights_mm list The heights of the rectangular ROIs in mm. Follows the order of roi_names. tolerance Tolerance of the slice thickness. slice_num Slice number of the thickness slice. avg_slice_thickness The average slice thickness for the 4 wire measurements in mm. nominal_slice_thickness The nominal slice thickness from the DICOM data. passed Whether the slice thickness was within tolerance from nominal. roi_heights The ROI heights, corrected for pixel spacing. roi_widths The ROI widths, corrected for pixel spacing. 6.6. API Documentation 43

class pylinac.cbct.roimanagermixin Bases: object Class for handling multiple ROIs. Used for the HU linearity, Uniformity, Geometry, Low-contrast, and Thickness slices. dist2rois_mm int, float The distance from the phantom center to the ROIs, in mm. roi_radius_mm int, float The radius of the ROIs, in mm. roi_names list The names of the ROIs. roi_nominal_angles list The nominal angles of the ROIs; must be same order as roi_names. roi_angles The ROI angles, corrected for phantom roll. dist2rois Distance from the phantom center to the ROIs, corrected for pixel spacing. roi_radius ROI radius, corrected for pixel spacing. get_roi_vals() Return a dict of the HU values of the HU ROIs. plot_rois(axis, threshold=none) Plot the ROIs to the axis. class pylinac.cbct.diskroi(array, angle, roi_radius, dist_from_center, phantom_center) Bases: pylinac.core.geometry.circle An class representing a disk-shaped Region of Interest. array (ndarray) The 2D array representing the image the disk is on. angle (int, float) The angle of the ROI in degrees from the phantom center. roi_radius (int, float) The radius of the ROI from the center of the phantom. dist_from_center (int, float) The distance of the ROI from the phantom center. phantom_center (tuple) The location of the phantom center. pixel_value The median pixel value of the ROI. std The standard deviation of the pixel values. 44 Chapter 6. CBCT module documentation

circle_mask() Return a mask of the image, only showing the circular ROI. class pylinac.cbct.hudiskroi(array, angle, roi_radius, dist_from_center, phantom_center, nominal_value=none, tolerance=none) Bases: pylinac.core.roi.diskroi An HU ROI object. Represents a circular area measuring either HU sample (Air, Poly,...) or HU uniformity (bottom, left,...). nominal_value (int) The nominal pixel value of the HU ROI. tolerance (int) The roi pixel value tolerance. value_diff The difference in HU between measured and nominal. passed Boolean specifying if ROI pixel value was within tolerance of the nominal value. plot_color Return one of two colors depending on if ROI passed. class pylinac.cbct.geodiskroi(array, angle, roi_radius, dist_from_center, phantom_center) Bases: pylinac.core.roi.diskroi A circular ROI, much like the HU ROI, but with methods to find the center of the geometric node. array (ndarray) The 2D array representing the image the disk is on. angle (int, float) The angle of the ROI in degrees from the phantom center. roi_radius (int, float) The radius of the ROI from the center of the phantom. dist_from_center (int, float) The distance of the ROI from the phantom center. phantom_center (tuple) The location of the phantom center. node_center Find the center of the geometric node within the ROI. class pylinac.cbct.lowcontrastdiskroi(array, angle, roi_radius, dist_from_center, phantom_center, contrast_threshold, background=none) Bases: pylinac.core.roi.diskroi A class for analyzing the low-contrast disks. contrast_threshold (float, int) The threshold for considering a bubble to be seen. contrast_to_noise The contrast to noise ratio of the bubble: (Signal - Background)/Stdev. contrast The contrast of the bubble compared to background: (ROI - backg) / (ROI + backg). contrast_constant The contrast value times the bubble diameter. passed_constant Boolean specifying if ROI pixel value was within tolerance of the nominal value. 6.6. API Documentation 45

plot_color Return one of two colors depending on if ROI passed. plot_color_constant Return one of two colors depending on if ROI passed. class pylinac.cbct.rectangleroi(array, width, height, angle, dist_from_center, phantom_center) Bases: pylinac.core.geometry.rectangle Class that represents a rectangular ROI. pixel_array The pixel array within the ROI. class pylinac.cbct.thicknessroi(array, width, height, angle, dist_from_center, phantom_center) Bases: pylinac.core.roi.rectangleroi A rectangular ROI that measures the angled wire rod in the HU linearity slice which determines slice thickness. long_profile The profile along the axis perpendicular to ramped wire. wire_fwhm The FWHM of the wire in pixels. plot_color The plot color. class pylinac.cbct.geometricline(geo_roi1, geo_roi2, mm_per_pixel, tolerance) Bases: pylinac.core.geometry.line Represents a line connecting two nodes/rois on the Geometry Slice. nominal_length_mm int, float The nominal distance between the geometric nodes, in mm. geo_roi1 (GEO_ROI) One of two ROIs representing one end of the line. geo_roi2 (GEO_ROI) The other ROI which is the other end of the line. mm_per_pixel (float) The mm/pixel value. tolerance (int, float) The tolerance of the geometric line, in mm. passed Whether the line passed tolerance. pass_fail_color Plot color for the line, based on pass/fail status. length_mm Return the length of the line in mm. 46 Chapter 6. CBCT module documentation

CHAPTER 7 Log Analyzer module documentation 7.1 Overview 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. Features: Analyze Dynalogs or Trajectory logs - Either platform is supported. Tlog versions 2.1 and 3.0 supported. Read in both.bin and.txt Trajectory log files - Read in the machine data from both.bin and.txt files to get all the information recorded. See txt. 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 accessed and plotted: the actual, expected, and even the difference. Calculate fluences and gamma - Besides reading in the MLC positions, pylinac calculates the actual and expected fluence as well as the gamma map; DTA and threshold values are adjustable. Anonymize logs - Both dynalogs and trajectory logs can be anonymized by removing the Patient ID from the filename(s) and file data. 7.2 Concepts Because the log_analyzer module functions without an end goal, the data has been formatted for easy exploration. However, there are a few concepts that should be grasped before diving in. Log Sections - Upon log parsing, all data is placed into data structures. Varian has designated 4 sections for Trajectory logs: Header, Axis Data, Subbeams, and CRC. The Subbeams are only applicable for auto-sequenced beams, and the CRC is specific to the Trajectory log. The Header and Axis Data however, are common to both Trajectory logs and Dynalogs. Note: Dynalogs do not have explicit sections like the Trajectory logs, but pylinac formats them to have these two data structures for consistency. 47

Leaf Indexing & Positions - Varian leaf identification is 1-index based, over against Python s 0-based indexing. Warning: When slicing or analyzing leaf data, keep the Varian 1-index base in mind. Leaf data is stored in a dictionary, with the leaf number as the key, from 1 up to the number of MLC leaves. E.g. if the machine has a Millennium 120 standard MLC model, leaf data will have 120 dictionary items from 1 to 120. Leaf numbers have an offset of half the number of leaves. I.e. leaves 1 and 120 are a pair, as are 2 and 119, on up to leaves 60 and 61. In such a case, leaves 61-120 correspond to the B-bank, while leaves 1-60 correspond to the A-bank. This can be described by a function (A leaf, B leaf ) = (n, N leaves + 1 n), where n is the leaf number and N leaves is the number of leaves. Units - Units follow the Trajectory log specification: linear axes are in cm, rotational axes in degrees, and MU for dose. Note: Dynalog files are inherently in mm for collimator and gantry axes, tenths of degrees for rotational axes, and MLC positions are not at isoplane. For consistency, Dynalog values are converted to Trajectory log specs, meaning linear axes, both collimator and MLCs are in cm at isoplane, and rotational axes are in degrees. Dynalog MU is always from 0 to 25000 no matter the delivered MU (i.e. it s relative). All Data Axes are similar - Log files capture machine data in control cycles, aka snapshots or heartbeats. Let s assume a log has captured 100 control cycles. Axis data that was captured will all be similar. They will all have an actual and sometimes an expected value for each cycle. Pylinac formats these as 1D numpy arrays along with a difference array if applicable. Each of these arrays can be quickly plotted for visual analysis. See Axis for more info. 7.3 Running the Demo As usual, the module comes with demo files and methods: >>> from pylinac import MachineLog >>> MachineLog().run_dlog_demo() Which will output the following: MLC log type: Dynalog Average RMS of all leaves: 0.074 cm Max RMS error of all leaves: 0.076 cm 95th percentile error: 0.088 cm Number of beam holdoffs: 20 Gamma pass %: 99.83 Gamma average: 0.021 48 Chapter 7. Log Analyzer module documentation

The same can be done using the demo Trajectory log: >>> MachineLog().run_tlog_demo() MLC log type: Trajectory log Average RMS of all leaves: 0.001 cm Max RMS error of all leaves: 0.002 cm 95th percentile error: 0.002 cm Number of beam holdoffs: 0 Gamma pass %: 100.00 Gamma average: 0.002 7.3. Running the Demo 49

7.4 Loading Data Logs can be loaded one of two ways: upon class initialization and through the load method: from pylinac import MachineLog log_path = "C:/path/to/tlog.bin" log = MachineLog(log_path) Or: log2 = MachineLog() log2.load(log_path) There are also ways to load logs from other sources. For example, load a log from a UI dialog box: log = MachineLog.from_UI() Or load from a URL: log = MachineLog.from_url('http://myserver.com/logs/1') Pylinac will automatically infer the log type and load it into data structures for analysis. 7.5 Working with the Data Working with the log data is straightforward once the data structures and Axes are understood (See Concepts for more info). Pylinac follows the data structures specified by Varian for trajectory logs, with a Header and Axis Data structure, and possibly a Subbeams structure if the log is a Trajectory log and was autosequenced. For accessible attributes, see MachineLog. The following sections explore each major section of log data and the data structures pylinac creates to assist in data analysis. 50 Chapter 7. Log Analyzer module documentation

Note: It may be helpful to also read the log specification format in parallel with this guide. It is easier to see that pylinac follows the log specifications and where the info comes from. Log specifications are on MyVarian.com. 7.5.1 Working with the Header Header information is essentially anything that isn t axis measurement data; it s metadata about the file, format, machine configuration, etc. Because of the different file formats, there are separate classes for Trajectory log and Dynalog headers. The classes are: TlogHeader DlogHeader Header attributes are listed in the class API docs by following the above links. For completeness they are also listed here. For Trajectory logs: header version header_size sampling_interval num_axes axis_enum samples_per_axis num_mlc_leaves axis_scale num_subbeams is_truncated num_snapshots mlc_model For Dynalogs the following header information is available: version patient_name plan_filename tolerance num_mlc_leaves clinac_scale Example Let s explore the header of the demo trajectory log: 7.5. Working with the Data 51

>>> tlog = MachineLog.from_demo_trajectorylog() >>> tlog.header.header 'VOSTL' >>> tlog.header.version 2.1 >>> tlog.header.num_subbeams 2 7.5.2 Working with Axis Data Axis data is all the information relating to the measurements of the various machine axes and is accessible under the axis_data attribute. This includes the gantry, collimator, MLCs, etc. Trajectory logs capture more information than Dynalogs, and additionally hold the expected positions not only for MLCs but also for all axes. Every measurement axis has Axis as its base; they all have similar methods to access and plot the data (see Plotting & Saving Axes/Fluences). However, not all attributes are axes. Pylinac adds properties to the axis data structure for ease of use (e.g. the number of snapshots) For Trajectory logs the following attributes are available, based on the TlogAxisData class: collimator gantry jaws Note: The jaws attribute is a data structure to hold all 4 jaw axes; see JawStruct couch Note: The couch attribute is a data structure to hold lateral, longitudinal, etc couch positions; see CouchStruct mu beam_hold control_point carriage_a carriage_b mlc Note: The mlc attribute is a data structure to hold leaf information; see MLC for attributes and the Working with MLC Data section for more info. Dynalogs have similar attributes, derived from the DlogAxisData class: collimator gantry jaws 52 Chapter 7. Log Analyzer module documentation

Note: The jaws attribute is a data structure to hold all 4 jaw axes; see JawStruct num_snapshots mu beam_hold beam_on previous_segment_num previous_dose_index next_dose_index carriage_a carriage_b mlc Note: The mlc attribute is a data structure to hold leaf information; see MLC for attributes and the Working with MLC Data section for more info. Example Let s access a few axis data attributes: >>> log = MachineLog.from_demo_dynalog() >>> log.axis_data.mu.actual # a numpy array array([ 0, 100,... >>> log.axis_data.num_snapshots 99 >>> log.axis_data.gantry.actual array([ 180, 180, 180,... 7.5.3 Working with MLC Data Although MLC data is acquired and included in Trajectory logs and Dynalogs, it is not always easy to parse. Additionally, a physicist may be interested in the MLC metrics of a log (RMS, etc). Pylinac provides tools for accessing MLC raw data as well as helper methods and properties via the MLC class. Note that this class is consistent between Trajectory logs and Dynalogs. This class is reachable through the axis_data attribute as mlc. Accessing Leaf data Leaf data for any leaf is available under the leaf_axes attribute which is a dict. The leaves are keyed by the leaf number and the value is an Axis. Example: >>> log = MachineLog.from_demo_trajectorylog() >>> log.axis_data.mlc.leaf_axes[1].actual # numpy array of the 'actual' values for leaf #1 array([ 7.56374,... >>> log.axis_data.mlc.leaf_axes[84].difference # actual values minus the planned values for leaf 84 array([-0.001966,... 7.5. Working with the Data 53

MLC helper methods/properties Beyond direct MLC data, pylinac provides a number of helper methods and properties to make working with MLC data easier and more helpful. All the methods are listed in the MLC class, but some examples of use are given here: >>> log = MachineLog.from_demo_dynalog() >>> log.axis_data.mlc.get_error_percentile(percentile=95) # get an MLC error percentile value 0.08847 >>> log.axis_data.mlc.leaf_moved(12) # did leaf 12 move during treatment? False >>> log.axis_data.mlc.get_rms_avg() # get the average RMS error 0.03733 >>> log.axis_data.mlc.get_rms_avg('a') # get the average RMS error for bank A 0.03746 >>> log.axis_data.mlc.num_leaves # the number of MLC leaves 120 >>> log.axis_data.mlc.num_moving_leaves # the number of leaves that moved during treatment 60 7.5.4 Working with Fluences Fluences created by the MLCs can also be accessed and viewed. Fluences are accessible under the fluence attribute. There are three subclasses that handle the fluences: The fluence actually delivered is in ActualFluence, the fluence planned is in ExpectedFluence, and the gamma of the fluences is in GammaFluence. Each fluence must be calculated, however pylinac makes reasonable defaults and has a few shortcuts. The actual and expected fluences can be calculated to any resolution in the leaf-moving direction. Some examples: >>> log = MachineLog.from_demo_dynalog() >>> log.fluence.actual.calc_map() # calculate the actual fluence; returns a numpy array array([ 0, 0,... >>> log.fluence.expected.calc_map(resolution=1) # calculate at 1mm resolution array([ 0, 0,... >>> log.fluence.gamma.calc_map(distta=0.5, doseta=1, resolution=0.1) array([ 0, 0,... >>> log.fluence.gamma.pass_prcnt # the gamma passing percentage 99.82 >>> log.fluence.gamma.avg_gamma # the average gamma value 0.0208 7.5.5 Plotting & Saving Axes/Fluences Each and every axis of the log can be accessed as a numpy array and/or plotted. For each axis the actual array/plot is always available. Dynalogs only have expected values for the MLCs. Trajectory logs have the actual and expected values for all axes. Additionally, if an axis has actual and expected arrays, then the difference is also available. Example of plotting the MU actual: log = MachineLog.from_demo_trajectorylog() log.axis_data.mu.plot_actual() Plot the Gantry difference: log.axis_data.gantry.plot_difference() Now lets plot fluences: 54 Chapter 7. Log Analyzer module documentation

log.fluence.actual.plot_map() log.fluence.gamma.plot_map() 7.5. Working with the Data 55

7.6 Converting Trajectory logs to CSV If you already have the log files, you obviously have a record of treatment. However, trajectory logs are in binary format and are not easily readable without tools like pylinac. You can save trajectory logs in a more readable format through the to_csv() method. This will write the log to a comma-separated variable (CSV) file, which can be read with Excel and many other programs. You can do further or specialized analysis with the CSV files if you wish, without having to use pylinac: log = MachineLog.from_demo_trajectorylog() log.to_csv() 7.7 Anonymizing Logs Machine logs can be anonymized using the anonymize() method, available to both MachineLog and MachineLogs. Example script: log = MachineLog.from_demo_trajectorylog() log.anonymize() 56 Chapter 7. Log Analyzer module documentation

7.8 Batch Processing Batch processing/loading of log files is helpful when dealing with one file at a time is too cumbersome. Pylinac allows you to load logs of an entire directory via MachineLogs; individual log files can be accessed, and a handful of batch methods are included. Example Let s assume all of your logs for the past week are in a folder. You d like to quickly see what the average gamma is of the files: >>> from pylinac import MachineLogs >>> log_dir = r"c:\path\to\log\directory" >>> logs = MachineLogs(log_dir) >>> logs.avg_gamma(resolution=0.2) 0.03 # or whatever You can also append to MachineLogs to have two or more different folders combined: >>> other_log_dir = r"c:\different\path" >>> logs.append(other_log_dir) Trajectory logs in a MachineLogs instance can also be converted to CSV, just as for a MachineLog: >>> logs.to_csv() # only converts trajectory logs; dynalogs are already basically CSV files Note: Batch processing methods (like avg_gamma() can take a while if numerous logs have been loaded, so be patient. You can also use the verbose=true argument in batch methods to see how the process is going. 7.9 API Documentation class pylinac.log_analyzer.machinelogs(folder=none, recursive=true) Bases: list New in version 0.4.1. Read in machine logs from a directory. Inherits from list. Batch methods are also provided. folder (str) The directory of interest. Will walk through and process any logs, Trajectory or dynalog, it finds. Non-log files will be skipped. recursive (bool) Whether to walk through subfolders of passed directory. Only used if folder is a valid log directory. Examples Load a directory upon initialization: >>> log_folder = r'c:\path\log\directory' >>> logs = MachineLogs(log_folder) 7.8. Batch Processing 57

Or load them later directly: >>> logs = MachineLogs() >>> logs.load_folder(log_folder) Batch methods include determining the average gamma and average gamma pass value: >>> logs.avg_gamma() >>> 0.05 # or whatever it is >>> logs.avg_gamma_pct() >>> 97.2 classmethod from_zip(zfile) Instantiate from a ZIP archive. zfile (str) Path to the zip archive. num_logs The number of logs currently loaded. num_tlogs The number of Trajectory logs currently loaded. num_dlogs The number of Trajectory logs currently loaded. load_folder(dir, recursive=true) Load log files from a directory. dir (str, None) The directory of interest. If a string, will walk through and process any logs, Trajectory or dynalog, it finds. Non-log files will be skipped. If None, files must be loaded later using.load_dir() or.append(). recursive (bool) If True (default), will walk through subfolders of passed directory. If False, will only search root directory. report_basic_parameters() Report basic parameters of the logs. Number of logs Average gamma value of all logs Average gamma pass percent of all logs append(obj, recursive=true) Append a log. Overloads list method. obj (str, MachineLog) If a string, must point to a log file. If a directory, must contain log files. If a MachineLog, then simply appends. recursive (bool) Whether to walk through subfolders of passed directory. Only applicable if obj was a directory. avg_gamma(doseta=1, distta=1, threshold=10, resolution=0.1) Calculate and return the average gamma of all logs. See calc_map() for further parameter info. avg_gamma_pct(doseta=1, distta=1, threshold=10, resolution=0.1) Calculate and return the average gamma pass percent of all logs. See calc_map() for further parameter info. 58 Chapter 7. Log Analyzer module documentation

to_csv() Write trajectory logs to CSV. If there are both dynalogs and trajectory logs, only the trajectory logs will be written. File names will be the same as the original log file names. Returns A list of all the filenames of the newly created CSV files. Return type list anonymize(inplace=false, suffix=none) Save an anonymized version of the log. For dynalogs, this replaces the patient ID in the filename(s) and the second line of the log with Anonymous<suffix>. This will rename both A* and B* logs if both are present in the same directory. For trajectory logs, the patient ID in the filename is replaced with Anonymous<suffix> for the.bin file. If the associated.txt file is in the same directory it will similarly replace the patient ID in the filename with Anonymous<suffix>. Additionally, the Patient ID row will be replaced with Patient ID: Anonymous<suffix>. Note: Anonymization is only available for logs loaded locally (i.e. not from a URL or a data stream). To anonymize such a log it must be first downloaded or written to a file, then loaded in. Note: Anonymization is done to the log file itself. The current instance(s) of MachineLog will not be anonymized. inplace (bool) If False (default), creates an anonymized copy of the log(s). If True, renames and replaces the content of the log file. suffix (str, optional) An optional suffix that is added after Anonymous to give specificity to the log. Returns A list containing the paths to the newly written files. Return type list class pylinac.log_analyzer.machinelog(filename=, exclude_beam_off=true) Bases: object Reads in and analyzes MLC log files, both dynalog and trajectory logs, from Varian linear accelerators. If reading Trajectory logs, the.txt file is also loaded if it s around. filename (str) Path to the log file. For trajectory logs this is a single.bin file. For dynalog files, load either the A*.dlg or B*.dlg file. Warning: Dynalogs must have names like A*.dlg and B*.dlg and be in the same folder, otherwise errors will ensue. exclude_beam_off (boolean) If True (default), snapshots where the beam was not on will be removed. If False, all data will be included. 7.9. API Documentation 59

Examples Load a file upon initialization: >>> mylogfile = "C:/path/to/log.bin" >>> log = MachineLog(mylogfile) Or: >>> mylogfile = "C:/path/to/A1.dlg" # B must also be here >>> log = MachineLog() >>> log.load(mylogfile) Or load from a UI dialog: >>> log = MachineLog.from_UI() Run the demo: >>> MachineLog.run_dlog_demo() >>> MachineLog.run_tlog_demo() filename str The name of the file loaded in. header A Tlog_Header or Dlog_Header, depending on the log type. axis_data Tlog_Axis_Data or Dlog_Axis_Data, depending on the log type. subbeams SubbeamHandler txt dict Will contain instances of Subbeam; will be empty if autosequencing was not done on v2.1. If working with trajectory logs and the associated.txt file is in the same directory, the txt file data will be loaded as a dictionary. fluence Fluence_Struct Contains actual and expected fluence data, including gamma. log_type str The log type loaded; either Dynalog or Trajectory log log_is_loaded bool Whether a log has yet been loaded. static run_tlog_demo() Run the Trajectory log demo. static run_dlog_demo() Run the dynalog demo. 60 Chapter 7. Log Analyzer module documentation

load_demo_dynalog(exclude_beam_off=true) Load the demo dynalog file included with the package. load_demo_trajectorylog(exclude_beam_off=true) Load the demo trajectory log included with the package. classmethod from_url(url, exclude_beam_off=true) Instantiate a log from a URL. New in version 0.7.1. load_url(url, exclude_beam_off=true) Load a log from a URL. New in version 0.7.1. load(filename, exclude_beam_off=true) Load the log file directly by passing the path to the file. filename (str) The path to the log file. exclude_beam_off (boolean) If True (default), snapshots where the beam was not on will be removed. If False, all data will be included. Warning: Including beam off data may affect fluence and gamma results. E.g. in a step-&-shoot IMRT delivery, leaves move between segments while the beam is held. If beam-off data is included, the RMS and fluence errors may not correspond to what was delivered. plot_summary(show=true) Plot actual & expected fluence, gamma map, gamma histogram, MLC error histogram, and MLC RMS histogram. save_summary(filename, **kwargs) Save the summary image to file. report_basic_parameters(printout=true) Print the common parameters analyzed when investigating machine logs: Log type Average MLC RMS Maximum MLC RMS 95th percentile MLC error Number of beam holdoffs Gamma pass percentage Average gamma value anonymize(inplace=false, destination=none, suffix=none) Save an anonymized version of the log. For dynalogs, this replaces the patient ID in the filename(s) and the second line of the log with Anonymous<suffix>. This will rename both A* and B* logs if both are present in the same directory. For trajectory logs, the patient ID in the filename is replaced with Anonymous<suffix> for the.bin file. If the associated.txt file is in the same directory it will similarly replace the patient ID in the filename 7.9. API Documentation 61

with Anonymous<suffix>. Additionally, the Patient ID row will be replaced with Patient ID: Anonymous<suffix>. Note: Anonymization is only available for logs loaded locally (i.e. not from a URL or a data stream). To anonymize such a log it must be first downloaded or written to a file, then loaded in. Note: Anonymization is done to the log file itself. The current instance of MachineLog will not be anonymized. inplace (bool) If False (default), creates an anonymized copy of the log(s). If True, renames and replaces the content of the log file. destination (str, optional) A string specifying the directory where the newly anonymized logs should be placed. If None, will place the logs in the same directory as the originals. suffix (str, optional) An optional suffix that is added after Anonymous to give specificity to the log. Returns A list containing the paths to the newly written files. Return type list log_type Determine the MLC log type: Trajectory or Dynalog. The file is opened and sampled of the first few bytes. If the sample matches what is found in standard dynalog or tlog files it will be assigned that log type. Returns str Return type { Dynalog, Trajectory Log } Raises ValueError : If log type cannot be determined. is_loaded Boolean specifying if a log has been loaded in yet. treatment_type The treatment type of the log. Possible options: Returns One of the following: * VMAT * Dynamic IMRT * Static IMRT Return type str to_csv(filename=none) Write the log to a CSV file. Only applicable for Trajectory logs (Dynalogs are already similar to CSV). filename (None, str) If None (default), the CSV filename will be the same as the filename of the log. If a string, the filename will be named so. Returns The full filename of the newly created CSV file. Return type str class pylinac.log_analyzer.axis(actual, expected=none) Bases: object 62 Chapter 7. Log Analyzer module documentation

Represents an Axis of a Trajectory log or dynalog file, holding actual and potentially expected and difference values. are Attributes actual (numpy.ndarray) The array of actual position values. expected (numpy.ndarray, optional) The array of expected position values. Not applicable for dynalog axes other than MLCs. difference Return an array of the difference between actual and expected positions. Returns Array the same length as actual/expected. Return type numpy.ndarray plot_actual(interactive=false) Plot the actual positions as a matplotlib figure. plot_expected(interactive=false) Plot the expected positions as a matplotlib figure. plot_difference(interactive=false) Plot the difference of positions as a matplotlib figure. class pylinac.log_analyzer.mlc(snapshot_idx=none, jaw_struct=none, HDMLC=False) Bases: object The MLC class holds MLC information and retrieves relevant data about the MLCs and positions. snapshot_idx (array, list) The snapshots to be considered for RMS and error calculations (can be all snapshots or just when beam was on). jaw_struct (Jaw_Struct) HDMLC (boolean) If False (default), indicates a regular MLC model (e.g. Millennium 120). If True, indicates an HD MLC model (e.g. Millennium 120 HD). leaf_axes dict containing Axis The dictionary is keyed by the leaf number, with the Axis as the value. Warning: Leaf numbers are 1-index based to correspond with Varian convention. num_pairs Return the number of MLC pairs. num_leaves Return the number of MLC leaves. num_snapshots Return the number of snapshots used for MLC RMS & Fluence calculations. Warning: This number may not be the same as the number of recorded snapshots in the log since the snapshots where the beam was off may not be included. See MachineLog.load() 7.9. API Documentation 63

num_moving_leaves Return the number of leaves that moved. moving_leaves Return an array of the leaves that moved during treatment. add_leaf_axis(leaf_axis, leaf_num) Add a leaf axis to the MLC data structure. leaf_axis (LeafAxis) The leaf axis to be added. leaf_num (int) The leaf number. Warning: Leaf numbers are 1-index based to correspond with Varian convention. leaf_moved(leaf_num) Return whether the given leaf moved during treatment. leaf_num (int) Warning: Leaf numbers are 1-index based to correspond with Varian convention. pair_moved(pair_num) Return whether the given pair moved during treatment. If either leaf moved, the pair counts as moving. pair_num (int) Warning: Pair numbers are 1-index based to correspond with Varian convention. get_rms_avg(bank= both, only_moving_leaves=false) Return the overall average RMS of given leaves. Returns bank ({ A, B, both }) Specifies which bank(s) is desired. only_moving_leaves (boolean) If False (default), include all the leaves. If True, will remove the leaves that were static during treatment. Warning: The RMS and error will nearly always be lower if all leaves are included since non-moving leaves have an error of 0 and will drive down the average values. Convention would include all leaves, but prudence would use only the moving leaves to get a more accurate assessment of error/rms. Return type float get_rms_max(bank= both ) Return the overall maximum RMS of given leaves. bank ({ A, B, both }) Specifies which bank(s) is desired. Returns Return type float 64 Chapter 7. Log Analyzer module documentation

get_rms_percentile(percentile=95, bank= both, only_moving_leaves=false) Return the n-th percentile value of RMS for the given leaves. percentile (int) RMS percentile desired. bank ({ A, B, both }) Specifies which bank(s) is desired. only_moving_leaves (boolean) If False (default), include all the leaves. If True, will remove the leaves that were static during treatment. Warning: The RMS and error will nearly always be lower if all leaves are included since non-moving leaves have an error of 0 and will drive down the average values. Convention would include all leaves, but prudence would use only the moving leaves to get a more accurate assessment of error/rms. get_rms(leaves_or_bank) Return an array of leaf RMSs for the given leaves or MLC bank. leaves_or_bank (sequence of numbers, { a, b, both }) If a sequence, must be a sequence of leaf numbers desired. If a string, it specifies which bank (or both) is desired. Returns An array for the given leaves containing the RMS error. Return type numpy.ndarray get_leaves(bank= both, only_moving_leaves=false) Return a list of leaves that match the given conditions. bank ({ A, B, both }) Specifies which bank(s) is desired. only_moving_leaves (boolean) If False (default), include all the leaves. If True, will remove the leaves that were static during treatment. get_error_percentile(percentile=95, bank= both, only_moving_leaves=false) Calculate the n-th percentile error of the leaf error. percentile (int) RMS percentile desired. bank ({ A, B, both }) Specifies which bank(s) is desired. only_moving_leaves (boolean) If False (default), include all the leaves. If True, will remove the leaves that were static during treatment. Warning: The RMS and error will nearly always be lower if all leaves are included since non-moving leaves have an error of 0 and will drive down the average values. Convention would include all leaves, but prudence would use only the moving leaves to get a more accurate assessment of error/rms. create_error_array(leaves, absolute=true) Create and return an error array of only the leaves specified. leaves (sequence) Leaves desired. 7.9. API Documentation 65

absolute (bool) If True, (default) absolute error will be returned. If False, error signs will be retained. Returns An array of size leaves-x-num_snapshots Return type numpy.ndarray create_rms_array(leaves) Create an RMS array of only the leaves specified. leaves (sequence) Leaves desired. Returns An array of size leaves-x-num_snapshots Return type numpy.ndarray leaf_under_y_jaw(leaf_num) Return a boolean specifying if the given leaf is under one of the y jaws. leaf_num (int) get_snapshot_values(bank_or_leaf= both, dtype= actual ) Retrieve the snapshot data of the given MLC bank or leaf/leaves bank_or_leaf (str, array, list) If a str, specifies what bank ( A, B, both ). If an array/list, specifies what leaves (e.g. [1,2,3]) dtype ({ actual, expected }) The type of MLC snapshot data to return. Returns An array of shape (number of leaves - x - number of snapshots). E.g. for an MLC bank and 500 snapshots, the array would be (60, 500). Return type ndarray plot_mlc_error_hist(show=true) Plot an MLC error histogram. save_mlc_error_hist(filename, **kwargs) Save the MLC error histogram to file. plot_rms_by_leaf(show=true) Plot RMSs by leaf. save_rms_by_leaf(filename, **kwargs) Save the RMS-leaf to file. class pylinac.log_analyzer.dlogheader(log_content) Bases: pylinac.log_analyzer.dlogsection The Header section of a dynalog file. version str The Dynalog version letter. patient_name str Patient information. plan_filename str 66 Chapter 7. Log Analyzer module documentation

Filename if using standalone. If using Treat =<6.5 will produce PlanUID, Beam Number. Not yet implemented for this yet. tolerance int Plan tolerance. num_mlc_leaves int Number of MLC leaves. clinac_scale int Clinac scale; 0 -> Varian scale, 1 -> IEC 60601-2-1 scale class pylinac.log_analyzer.dlogaxisdata(log_content, header, bfile) Bases: pylinac.log_analyzer.dlogsection Axis data for dynalogs. num_snapshots int mu Number of snapshots recorded. Axis Current dose fraction Note: This can be gantry rotation under certain conditions. See Dynalog file specs. previous_segment_num Axis Previous segment number, starting with zero. beam_hold Axis Beam hold state; 0 -> holdoff not asserted (beam on), 1 -> holdoff asserted, 2 -> carriage in transition beam_on Axis Beam on state; 1 -> beam is on, 0 -> beam is off previous_dose_index Axis Previous segment dose index or previous segment gantry angle. next_dose_index Axis Next segment dose index. gantry Axis Gantry data in degrees. 7.9. API Documentation 67

collimator Axis Collimator data in degrees. jaws Jaw_Struct Jaw data structure. Data in cm. carriage_a Axis Carriage A data. Data in cm. carriage_b Axis mlc MLC Carriage B data. Data in cm. MLC data structure. Data in cm. num_beamholds Return the number of times the beam was held. class pylinac.log_analyzer.tlogheader(log_content, cursor) Bases: pylinac.log_analyzer.tlogsection A header object, one of 4 sections of a trajectory log. Holds sampling interval, version, etc. header str version str Header signature: VOSTL. Log version. header_size int Header size; fixed at 1024. sampling_interval int Sampling interval in milliseconds. num_axes int Number of axes sampled. axis_enum int Axis enumeration; see the Tlog file specification for more info. samples_per_axis numpy.ndarray Number of samples per axis; 1 for most axes, for MLC it s # of leaves and carriages. 68 Chapter 7. Log Analyzer module documentation

num_mlc_leaves int Number of MLC leaves. axis_scale int Axis scale; 1 -> Machine scale, 2 -> Modified IEC 61217. num_subbeams int Number of subbeams, if autosequenced. is_truncated int Whether log was truncated due to space limitations; 0 -> not truncated, 1 -> truncated num_snapshots int Number of snapshots, cycles, heartbeats, or whatever you d prefer to call them. mlc_model int The MLC model; 2 -> NDS 120 (e.g. Millennium), 3 -> NDS 120 HD (e.g. Millennium 120 HD) class pylinac.log_analyzer.tlogaxisdata(log_content, cursor, header) Bases: pylinac.log_analyzer.tlogsection One of four data structures outlined in the Trajectory log file specification. Holds information on all Axes measured. collimator Axis Collimator data in degrees. gantry Axis Gantry data in degrees. jaws Jaw_Struct Jaw data structure. Data in cm. couch Couch_Struct mu Couch data structure. Data in cm. Axis MU data in MU. beam_hold Axis 7.9. API Documentation 69

Beam hold state. Beam pauses (e.g. Beam Off button pressed) are not recorded in the log. Data is automatic hold state. 0 -> Normal; beam on. 1 -> Freeze; beam on, dose servo is temporarily turned off. 2 -> Hold; servo holding beam. 3 -> Disabled; beam on, dose servo is disable via Service. control_point Axis Current control point. carriage_a Axis Carriage A data in cm. carriage_b Axis mlc MLC Carriage B data in cm. MLC data structure; data in cm. num_beamholds Return the number of times the beam was held. class pylinac.log_analyzer.subbeam(log_content, cursor, log_version) Bases: pylinac.log_analyzer.tlogsection Data structure for trajectory log subbeams. Only applicable for auto-sequenced beams. control_point int Internally-defined marker that defines where the plan is currently executing. mu_delivered float Dose delivered in units of MU. rad_time float Radiation time in seconds. sequence_num int Sequence number of the subbeam. beam_name str Name of the subbeam. gantry_angle Median gantry angle of the subbeam. collimator_angle Median collimator angle of the subbeam. jaw_x1 Median X1 position of the subbeam. 70 Chapter 7. Log Analyzer module documentation

jaw_x2 Median X2 position of the subbeam. jaw_y1 Median Y1 position of the subbeam. jaw_y2 Median Y2 position of the subbeam. class pylinac.log_analyzer.fluencestruct(mlc_struct=none, jaw_struct=none) Bases: object Structure for data and methods having to do with fluences. actual Fluence The actual fluence delivered. expected Fluence The expected, or planned, fluence. gamma GammaFluence The gamma structure regarding the actual and expected fluences. mu_axis=none, class pylinac.log_analyzer.fluence(mlc_struct=none, mu_axis=none, jaw_struct=none) Bases: object An abstract base class to be used for the actual and expected fluences. pixel_map numpy.ndarray An array representing the fluence map; will be num_mlc_pairs-x-400/resolution. E.g., assuming a Millennium 120 MLC model and a fluence resolution of 0.1mm, the resulting matrix will be 60-x-4000. resolution int, float The resolution of the fluence calculation; -1 means calculation has not been done yet. pixel_map alias of object mlc_struct (MLC_Struct) mu_axis (BeamAxis) jaw_struct (Jaw_Struct) map_calced Return a boolean specifying whether the fluence has been calculated. calc_map(resolution=0.1) Calculate a fluence pixel map. Fluence calculation is done by adding fluence snapshot by snapshot, and leaf pair by leaf pair. Each leaf pair is analyzed separately. First, to optimize, it checks if the leaf is under the y-jaw. If so, the fluence is 7.9. API Documentation 71

left at zero; if not, the leaf (or jaw) ends are determined and the MU fraction of that snapshot is added to the total fluence. All snapshots are iterated over for each leaf pair until the total fluence matrix is built. resolution (int, float) The resolution in mm of the fluence calculation in the leaf-moving direction. returns A numpy array reconstructing the actual fluence of the log. The size will be the number of MLC pairs by 400 / resolution since the MLCs can move anywhere within the 40cm-wide linac head opening. rtype numpy.ndarray plot_map(show=true) Plot the fluence; the fluence (pixel map) must have been calculated first. save_map(filename, **kwargs) Save the fluence map figure to a file. class pylinac.log_analyzer.actualfluence(mlc_struct=none, jaw_struct=none) Bases: pylinac.log_analyzer.fluence The actual fluence object mlc_struct (MLC_Struct) mu_axis (BeamAxis) jaw_struct (Jaw_Struct) class pylinac.log_analyzer.expectedfluence(mlc_struct=none, jaw_struct=none) Bases: pylinac.log_analyzer.fluence The expected fluence object. mlc_struct (MLC_Struct) mu_axis (BeamAxis) jaw_struct (Jaw_Struct) class pylinac.log_analyzer.gammafluence(actual_fluence, expected_fluence, mlc_struct) Bases: pylinac.log_analyzer.fluence Gamma object, including pixel maps of gamma, binary pass/fail pixel map, and others. pixel_map numpy.ndarray The gamma map. Only available after calling calc_map() passfail_map numpy.ndarray mu_axis=none, mu_axis=none, The gamma pass/fail map; pixels that pass (<1.0) are set to 0, while failing pixels (>=1.0) are set to 1. distta int, float The distance to agreement value used in gamma calculation. 72 Chapter 7. Log Analyzer module documentation

doseta int, float The dose to agreement value used in gamma calculation. threshold int, float The threshold percent dose value, below which gamma was not evaluated. pass_prcnt float The percent of pixels passing gamma (<1.0). avg_gamma float The average gamma value. passfail_map alias of object actual_fluence (ActualFluence) The actual fluence object. expected_fluence (ExpectedFluence) The expected fluence object. mlc_struct (MLC_Struct) The MLC structure, so fluence can be calculated from leaf positions. calc_map(doseta=1, distta=1, threshold=10, resolution=0.1, calc_individual_maps=false) Calculate the gamma from the actual and expected fluences. The gamma calculation is based on Bakai et al eq.6, which is a quicker alternative to the standard Low gamma equation. DoseTA (int, float) Dose-to-agreement in percent; e.g. 2 is 2%. DistTA (int, float) Distance-to-agreement in mm. threshold (int, float) The dose threshold percentage of the maximum dose, below which is not analyzed. resolution (int, float) The resolution in mm of the resulting gamma map in the leaf-movement direction. calc_individual_maps (bool) Not yet implemented. If True, separate pixel maps for the distance-to-agreement and dose-to-agreement are created. Returns A num_mlc_leaves-x-400/resolution numpy array. Return type numpy.ndarray plot_map(show=true) Plot the fluence; the fluence (pixel map) must have been calculated first. histogram(bins=none) Return a histogram array and bin edge array of the gamma map values. bins (sequence) The bin edges for the gamma histogram; see numpy.histogram for more info. 7.9. API Documentation 73

Returns histogram (numpy.ndarray) A 1D histogram of the gamma values. bin_edges (numpy.ndarray) A 1D array of the bin edges. If left as None, the class default will be used (self.bins). plot_histogram(scale= log, bins=none, show=true) Plot a histogram of the gamma map values. scale ({ log, linear }) Scale of the plot y-axis. bins (sequence) The bin edges for the gamma histogram; see numpy.histogram for more info. save_histogram(filename, scale= log, bins=none, **kwargs) Save the histogram plot to file. plot_passfail_map() Plot the binary gamma map, only showing whether pixels passed or failed. class pylinac.log_analyzer.jawstruct(x1, y1, x2, y2) Bases: object Jaw Axes data structure. x1 y1 x2 y2 Axis Axis Axis Axis class pylinac.log_analyzer.couchstruct(vertical, longitudinal, lateral, rotational, roll=none, pitch=none) Bases: object Couch Axes data structure. vert Axis long Axis latl Axis rotn Axis 74 Chapter 7. Log Analyzer module documentation

CHAPTER 8 Picket Fence module documentation 8.1 Overview 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 can load in an EPID dicom image (or superimpose multiple images) and determine the MLC peaks, error of each MLC pair to the picket, and give a few visual indicators for passing/warning/failing. Features: 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. 8.2 Concepts Although most terminology will be familiar to a clinical physicist, it is still helpful to be clear about what means what. A picket is the line formed by several MLC pairs all at the same position. There is usually some ideal gap between the MLCs, such as 0.5, 1, or 2 mm. An MLC position is, for pylinac s purposes, the center of the FWHM of the peak formed by one MLC pair at one picket. Thus, one picket fence image may have anywhere between a few to a dozen pickets, formed by as few as 10 MLC pairs up to all 60 pairs. Pylinac presents the analyzed image in such a way that allows for quick assessment; additionally, all elements atop the image can optionally be turned off. Pylinac by default will plot the image, the determined MLC positions, two guard rails, and a semi-transparent overlay over the entire MLC pair region. The guard rails are two lines parallel to the fitted picket, offset by the tolerance passed to analyze(). Thus, if a tolerance of 0.5 mm is passed, each guard rail is 0.5 mm to the left and right of the invisible picket. Ideally, MLC positions will all be within these guard rails, i.e. within tolerance, and will be colored blue. If they are outside the tolerance they are turned red. If an action tolerance is also passed to analyze(), MLC positions that are below tolerance but above the action tolerance are turned magenta. Additionally, pylinac provides a semi-transparent colored overlay so that an all clear or a pair(s) failed status is easily seen and not inadvertently overlooked. If any MLC position is outside the action tolerance or the absolute 75

tolerance, the entire MLC pair area is colored the corresponding color. In this way, not every position needs be looked at. If all rows are green, then all positions passed. 8.3 Running the Demo To run the picketfence demo, create a script or start in interpreter and input: from pylinac import PicketFence PicketFence().run_demo() Results will be printed to the console and a figure showing the analyzed picket fence image will pop up: Picket Fence Results: 100.0% Passed Median Error: 0.062mm Max Error: 0.208mm on Picket: 3, Leaf: 22 76 Chapter 8. Picket Fence module documentation

Or plot the figure interactively: PicketFence().run_demo(interactive=True) Note the Home/Pan/Zoom tools in the corner when hovering. Additionally, hovering over a leaf pair shows the error. Note: MPLD3 currently does not show images/arrays well, so you may not be able to see the underlying EPID image. If you just want to use the demo image without doing analysis: 8.3. Running the Demo 77

pf = PicketFence.from_demo_image() 8.4 Acquiring the Image The easiest way to acquire a picket fence image is using the EPID. In fact, pylinac will only analyze images acquired via an EPID, as the DICOM image it produces carries important information about the SID, pixel/mm conversion, etc. Depending on the EPID type and physicist, either the entire array of MLCs can be imaged at once, or only the middle leaves are acquired. Changing the SID can also change how many leaves are imaged. For analysis by pylinac, the SID does not matter, nor EPID type, nor panel translation. 8.5 Typical Use Picket Fence tests are recommended to be done weekly. With automatic software analysis, this can be a trivial task. Once the test is delivered to the EPID, retrieve the DICOM image and save it to a known location. Then import the class: from pylinac import PicketFence The minimum needed to get going is to: Load the image As with most other pylinac modules, loading images can be done by passing the image string directly, or by using a UI dialog box to retrieve the image manually. The code might look like either of the following: pf_img = r"c:/qa Folder/June/PF_6_21.dcm" pf = PicketFence(pf_img) Or, load using a UI dialog box: pf = PicketFence.from_image_UI() # UI dialog will pop up You may also load multiple images that become superimposed (e.g. an MLC & Jaw irradiation): img1 = r'path/to/image1.dcm' img2 = r'path/to/image2.dcm' pf = PicketFence.from_multiple_images([img1, img2]) Note: In previous versions of pylinac, loading images was instance-method based. This behavior has been simplified in favor of initialization normalization and adding class-method constructors (PicketFence.from_X). The reason for this is that certain actions should only be allowed until after the image is loaded. Furthermore, loading the image should always be the first action of the analysis sequence. By using class constructors, certain pitfalls and errors can be avoided. Don t worry though, the old behavior still works. Analyze the image Once the image is loaded, tell PicketFence to start analyzing the image. See the Algorithm section for details on how this is done. While defaults exist, you may pass in a tolerance as well as an action tolerance (meaning that while passing, action should be required above this tolerance): pf.analyze(tolerance=0.15, action_tolerance=0.03) # tight tolerance to demo fail & warning over View the results The PicketFence class can print out the summary of results to the console as well as draw a matplotlib image to show the image, MLC peaks, guard rails, and a color overlay for quick assessment: 78 Chapter 8. Picket Fence module documentation

# print results to the console print(pf.return_results()) # view analyzed image pf.plot_analyzed_image() which results in: The plot is also able to be saved: pf.save_analyzed_image('mypf.png') 8.6 Tips & Tricks Using the picketfence module in your own scripts? While the analysis results can be printed out, if you intend on using them elsewhere, they can be accessed through properties. Continuing from above: pf.max_error # max error in mm pf.max_error_picket # which picket contained the max error pf.max_error_leaf # which leaf contained the maximum error pf.abs_median_error # the absolute median error of all the leaves pf.num_pickets # how many pickets were found pf.percent_passing # the percent of MLC measurements below tolerance The EPID can also sag at certain angles. Because pylinac assumes a perfect panel, sometimes the analysis will not be centered exactly on the MLC leaves. If you want to correct for this, simply pass the EPID sag in mm: 8.6. Tips & Tricks 79

pf = PicketFence(r'C:/path/saggyPF.dcm') pf.analyze(sag_adjustment=0.6) 8.7 Algorithm The picket fence algorithm uses expected lateral positions of the MLCs and samples those regions for the center of the FWHM to determine the MLC positions: Allowances The image can be any size. Various leaf sizes can be analyzed (e.g. 5 and 10mm leaves for standard Millennium). Either standard or HD MLCs can be analyzed. The image can be either orientation (pickets going up-down or left-right). The image can be at any SSD. Any EPID type can be used (as500, as1000, as1200). The EPID panel can have an x or y offset (i.e. translation). Restrictions Warning: Analysis can fail or give unreliable results if any Restriction is violated. The image must be a DICOM image acquired via the EPID. Only Varian MLC models are supported (5/10mm or 2.5/5mm leaf combinations). The delivery must be parallel to an image edge; i.e. the collimator should be at 0, 90, or -90 degrees. Pre-Analysis Check for noise Dead pixels can cause wild values in an otherwise well-behaved image. These values can disrupt analysis, but pylinac will try to detect the presence of noise and will apply a median filter if detected. Check image inversion Upon loading, the image is sampled near all 4 corners for pixel values. If it is greater than the mean pixel value of the entire image the image is inverted. Determine orientation The image is summed along each axis. Pixel percentile values of each axis sum are sampled. The axis with a greater difference in percentile values is chosen as the orientation (The picket axis, it is argued, will have more pixel value variation than the axis parallel to leaf motion.) Adjust for EPID sag If a nonzero value is passed for the sag adjustment, the image is shifted along the axis of the pickets; i.e. a +1 mm adjustment for an Up-Down picket image will move expected MLC positions up 1 mm. Analysis Find the pickets The mean profile of the image perpendicular to the MLC travel direction is taken. Major peaks are assumed to be pickets. Find FWHM at each MLC position For each picket, a sample of the image in the MLC travel direction is taken at each MLC position. The center of the FWHM of the picket for that MLC position is recorded. Fit the picket to the positions & calculate error Once all the MLC positions are determined, the positions from each peak of a picket are fitted to a 1D polynomial which is considered the ideal picket. Differences of each MLC position to the picket polynomial fit at that position are determined, which is the error. When plotted, errors are tested against the tolerance and action tolerance as appropriate. 80 Chapter 8. Picket Fence module documentation

8.8 Troubleshooting First, check the general Troubleshooting section. Specific to the picket fence analysis, there are a few things you can do. Ensure the HDMLC status - If your image is from an HD MLC, you need to set the hdmlc parameter in analyze() to True, and vic versa. Apply a filter upon load - While pylinac tries to correct for unreasonable noise in the image before analysis, there may still be noise that causes analysis to fail. A way to check this is by applying a median filter upon loading the image: pf = PicketFence('mypf.dcm', filter=5) # vary the filter size depending on the image Then try performing the analysis. Check for streak artifacts - It is possible in certain scenarios (e.g. TrueBeam dosimetry mode) to have noteworthy artifacts in the image like so: If the artifacts are in the same direction as the pickets then it is possible pylinac is tripping on these artifacts. You can reacquire the image in another mode or simply try again in the same mode. You may also try cropping the image to exclude the artifact: pf = PicketFence('mypf.dcm') pf.image.array = mypf.image.array[200:400, 150:450] # or whatever values you want Set the number of pickets - If pylinac is catching too many pickets you can set the number of pickets to find with analyze(). 8.8. Troubleshooting 81

8.9 API Documentation class pylinac.picketfence.picketfence(filename=none, filter=none) Bases: object A class used for analyzing EPID images where radiation strips have been formed by the MLCs. The strips are assumed to be parallel to one another and normal to the image edge; i.e. a left-right or up-down orientation is assumed. Further work could follow up by accounting for any angle. pickets PicketHandler image Image Examples Run the demo:: >>> PicketFence().run_demo() Typical session: >>> img_path = r"c:/qa/june/pf.dcm" # the EPID image >>> mypf = PicketFence(img_path) >>> mypf.analyze(tolerance=0.5, action_tolerance=0.3) >>> print(mypf.return_results()) >>> mypf.plot_analyzed_image() filename (str, None) Name of the file as a string. If None, image must be loaded later. filter (int, None) The median filter size to apply to the image upon load. classmethod from_url(url, filter=none) Instantiate from a URL. New in version 0.7.1. load_url(url, filter=none) Load from a URL. New in version 0.7.1. passed Boolean specifying if all MLC positions were within tolerance. percent_passing Return the percentage of MLC positions under tolerance. max_error Return the maximum error found. max_error_picket Return the picket number where the maximum error occurred. max_error_leaf Return the leaf that had the maximum error. 82 Chapter 8. Picket Fence module documentation

abs_median_error Return the median error found. num_pickets Return the number of pickets determined. classmethod from_demo_image(filter=none) Construct a PicketFence instance using the demo image. New in version 0.6. load_demo_image(filter=none) Load the demo image that is included with pylinac. load_image(file_path, filter=none) Load the image file_path (str) Path to the image file. filter (int, None) If None (default), no filtering will be done to the image. If an int, will perform median filtering over image of size filter. classmethod from_multiple_images(path_list) Load and superimpose multiple images and instantiate a Starshot object. New in version 0.9. path_list (iterable) An iterable of path locations to the files to be loaded/combined. load_multiple_images(path_list) Load and superimpose multiple images. New in version 0.9. path_list (iterable) An iterable of path locations to the files to be loaded/combined. static run_demo(tolerance=0.5, action_tolerance=0.25, interactive=false) Run the Picket Fence demo using the demo image. See analyze() for parameter info. analyze(tolerance=0.5, action_tolerance=none, hdmlc=false, num_pickets=none, sag_adjustment=0) Analyze the picket fence image. tolerance (int, float) The tolerance of difference in mm between an MLC pair position and the picket fit line. action_tolerance (int, float, None) If None (default), no action tolerance is set or compared to. If an int or float, the MLC pair measurement is also compared to this tolerance. Must be lower than tolerance. This value is usually meant to indicate that a physicist should take an action to reduce the error, but should not stop treatment. hdmlc (bool) If False (default), a standard (5/10mm leaves) Millennium MLC model is assumed. If True, an HD (2.5/5mm leaves) Millennium is assumed. num_pickets (int, None) New in version 0.8. The number of pickets in the image. A helper parameter to limit the total number of pickets, only needed if analysis is catching more pickets than there really are. 8.9. API Documentation 83

sag_adjustment (float, int) New in version 0.8. The amount of shift in mm to apply to the image to correct for EPID sag. For Up-Down picket images, positive moves the image down, negative up. For Left-Right picket images, positive moves the image left, negative right. plot_analyzed_image(guard_rails=true, mlc_peaks=true, overlay=true, leaf_error_subplot=true, interactive=false, show=true) Plot the analyzed image. guard_rails (bool) Do/don t plot the picket guard rails around the ideal picket mlc_peaks (bool) Do/don t plot the MLC positions. overlay (bool) Do/don t plot the alpha overlay of the leaf status. leaf_error_subplot (bool) New in version 1.0. If True, plots a linked leaf error subplot adjacent to the PF image plotting the average and standard deviation of leaf error. interactive (bool) New in version 1.0. Note: mpld3 must be installed to use this feature. If False (default), plots a matplotlib figure. If True, plots a MPLD3 local server image, which adds some tooltips. save_analyzed_image(filename, guard_rails=true, mlc_peaks=true, overlay=true, leaf_error_subplot=false, interactive=false, **kwargs) Save the analyzed figure to a file. See plot_analyzed_image() for further parameter info. interactive [bool] If False (default), saves the figure as a.png image. If True, saves an html file, which can be opened in a browser, etc. Note: mpld3 must be installed to use this feature. return_results() Return results of analysis. Use with print(). orientation The orientation of the image, either Up-Down or Left-Right. Supporting Data Structures class pylinac.picketfence.pickethandler(image, settings, num_pickets) Bases: object Finds and handles the pickets of the image. error_hist() Returns several lists of information about the MLC measurements. For use with plotting. find_pickets() Find the pickets of the image. passed Whether all the pickets passed tolerance. 84 Chapter 8. Picket Fence module documentation

image_mlc_inplane_mean_profile A profile of the image along the MLC travel direction. class pylinac.picketfence.settings(orientation, tolerance, action_tolerance, hdmlc, image) Bases: object Simple class to hold various settings and info for PF analysis/plotting. figure_size The size of the figure to draw; depends on the picket orientation. small_leaf_width The width of a small leaf in pixels. large_leaf_width The width of a large leaf in pixels. number_small_leaves The number of small leaves; depends on HDMLC status. number_large_leaves The number of large leaves; depends on HDMLC status. leaf_centers Return a set of leaf centers perpendicular to the leaf motion based on the position of the CAX. class pylinac.picketfence.picket(image, settings, approximate_idx, spacing) Bases: object Holds picket information in a Picket Fence test. mlc_meas list Holds MLCMeas objects. find_mlc_peak(mlc_center) Determine the center of the picket. add_mlc_meas(mlc_center, mlc_position) Add an MLC measurement point. sample_width The width to sample the MLC leaf (~40% of the leaf width). picket_array A slice of the whole image that contains the area around the picket. abs_median_error The absolute median error of the MLC measurements. max_error The max error of the MLC measurements. error_array An array containing the error values of all the measurements. passed Whether or not all the measurements passed. mlc_passed(mlc) Return whether a specific MLC has passed tolerance. mlc_passed_action(mlc) Return whether a specific MLC has passed the action tolerance. 8.9. API Documentation 85

fit The fit of a polynomial to the MLC measurements. left_guard The line representing the left side guard rail. right_guard The line representing the right side guard rail. add_guards_to_axes(axis, color= g ) Plot guard rails to the axis. class pylinac.picketfence.mlcmeas(point1, point2, settings) Bases: pylinac.core.geometry.line Represents an MLC measurement. plot2axes(axes, width=1) Plot the measurement to the axes. bg_color The color of the measurement when the PF image is plotted, based on pass/fail status. error The error (difference) of the MLC measurement and the picket fit. passed Whether the MLC measurement was under tolerance. passed_action Whether the MLC measurement was under the action level tolerance. leaf_pair The leaf pair that formed the MLC measurement. Returns tuple Return type 2 elements which are the two leaf numbers class pylinac.picketfence.overlay(image, settings, pickets) Bases: object Class for handling the overlay feature of the plot. add_to_axes(axes) Add the overlay to the axes. 86 Chapter 8. Picket Fence module documentation

CHAPTER 9 Winston-Lutz module documentation 9.1 Overview The Winston-Lutz module loads and processes EPID images that have acquired Winston-Lutz type images. Features: 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. Gantry sag - The sag of the gantry is also quantified and can be plotted. 9.2 Running the Demo To run the Winston-Lutz demo, create a script or start an interpreter session and input: from pylinac import WinstonLutz WinstonLutz().run_demo() Results will be printed to the console and a figure showing the zoomed-in images will be generated: Winston-Lutz Analysis Number of images: 17 Maximum 2D CAX->BB distance: 1.25mm Median 2D CAX->BB distance: 0.62mm Gantry 3D isocenter radius: 0.67mm Gantry iso->bb vector: Vector(x=0.22, y=0.10, z=0.38) Gantry sag in the z-direction: 1.01mm Collimator 2D isocenter radius: 0.51mm Collimator 2D iso->bb vector: Vector(x=0.17, y=0.38, z=0.00) Couch 2D isocenter radius: 1.20mm Couch 2D iso->bb vector: Vector(x=-0.94, y=-0.35, z=0.00) 87

9.3 Image Acquisition The Winston-Lutz module will only load EPID images. The images can be from any EPID however, and any SID. To ensure the most accurate results, a few simple tips should be followed. Note that these are not unique to pylinac; most Winston-Lutz analyses require these steps: The BB should be fully within the field of view. The MLC field should be symmetric. 9.4 Coordinate Space When interpreting results from a Winston-Lutz test, it s important to know the coordinates, origin, etc. Pylinac uses the same coordinate space as Winkler et al. All coordinates are looking from the foot of the table toward the gantry: X-axis - Lateral, or left-right, with right being positive. Y-axis - Anterior-Posterior, or up-down, with up being positive. Z-axis - Cranio-Caudal, or in-out, with out being positive. 88 Chapter 9. Winston-Lutz module documentation

9.5 Typical Use Analyzing a Winston-Lutz test is as simple as loading in your images. So, let s import the class: from pylinac import WinstonLutz From here, you can load a directory: my_directory = 'path/to/wl_images' wl = WinstonLutz(my_directory) And that s it! From here, you can view images, gantry sag, or print the results: wl.plot_images() wl.plot_gantry_sag() # plot an individual image wl.images[3].plot() # save a figure of the image plots wl.save_plots('wltest.png') # return the gantry isocenter relative to the BB wl.gantry_iso2bb_vector 9.6 Algorithm The Winston-Lutz algorithm is based on the works of Winkler et al and Du et al. Winkler found that the collimator and couch iso could be found using a minimum optimization of the field CAX points. They also found that the gantry isocenter could by found by backprojecting the field CAX as a line in 3D coordinate space, with the BB being the reference point. The algorithm works like such: Allowances The images can be acquired with any EPID (as500, as1000, as1200) at any SID. The BB does not need to be near the real isocenter to determine isocenter sizes or gantry isocenter, but does affect the 2D image analysis. Restrictions Warning: Analysis can fail or give unreliable results if any Restriction is violated. The BB must be fully within the field of view. The images must be acquired with the EPID. Analysis Find the field CAX The spread in pixel values (max - min) is divided by 2, and any pixels above the threshold is associated with the open field. The pixels are converted to black & white and the center of mass of the pixels is assumed to be the field CAX. Find the BB The image is converted to binary based on pixel values both above the 50% threshold as above, and below the upper threshold. The upper threshold is an iterative value, starting at the image maximum value, that is lowered slightly when the BB is not found. If the binary image has a reasonably circular ROI, the BB is considered found and the pixel-weighted center of mass of the BB is considered the BB location. Note: Strictly speaking, the following aren t required analyses, but are explained for fullness and clarity. 9.5. Typical Use 89

Backproject the CAX for gantry images Based on the vector of the BB to the field CAX and the gantry angle, a 3D line projection of the CAX is constructed. The BB is considered at the origin. Only images where the collimator and couch were at 0 are used for CAX projection lines. Determine gantry isocenter size and location - Using the backprojection lines, an optimization function is run to minimize the maximum distance to any line. The optimized distance is the isocenter radius, and the point of maximum minimization is the isocenter location. Determine collimator isocenter size - The same optimization is run for all collimator images using the field CAX, but is only run in 2D, since the collimator only widens the isocenter size in the plane normal to the gantry angle. Determine couch isocenter size - Instead of using the BB as the non-moving reference point, which is now moving with the couch, the Reference image (gantry = collimator = couch = 0) CAX location is the reference. The movement of the BB relative to the Reference image CAX will form a semicircle (assuming the BB cannot be placed exactly at isocenter). The optimization is run to minimize the distance of each point to the edge of a circle. The optimum radius will determine the isocenter size. Note: Collimator iso size is always in the plane normal to the gantry, while couch iso size is always in the x-z plane. 9.7 API Documentation class pylinac.winston_lutz.winstonlutz(directory) Bases: object Class for performing a Winston-Lutz test of the radiation isocenter. directory (str) Path to the directory of the Winston-Lutz EPID images. Examples Load a directory with Winston-Lutz EPID images: >>> wl = WinstonLutz('path/to/directory') Load from a zip file: >>> wl = WinstonLutz.from_zip('path/to/images.zip') Or use the demo images provided: >>> wl = WinstonLutz.from_demo_images() images ImageManager instance static run_demo() Run the Winston-Lutz demo, which loads the demo files, prints results, and plots a summary image. classmethod from_demo_images() Instantiate using the demo images. classmethod from_zip(zfile) Instantiate from a zip file rather than a directory. zfile (str) Path to the archive file. 90 Chapter 9. Winston-Lutz module documentation

gantry_iso_size The radius of the 3D gantry isocenter size in mm. Only images where the collimator and couch were at 0 are used to determine this value. gantry_iso2bb_vector The 3D vector from the isocenter to the BB (located at the origin). collimator_iso_size The 2D collimator isocenter size in mm. The iso size is in the plane normal to the gantry. collimator_iso2bb_vector The 2D vector from the collimator isocenter to the BB (located at the origin). couch_iso_size The radius of the 2D couch isocenter size in mm. Only images where the gantry and collimator were at zero are used to determine this value. couch_iso2bb_vector The 2D vector from the couch isocenter to the BB (located at the origin). gantry_sag(axis= z ) The range of gantry sag along the given axis. cax2bb_distance(metric= max ) The distance in mm between the CAX and BB for all images according to the given metric. metric ({ max, median }) The metric of distance to use. plot_gantry_sag(ax=none, show=true) Plot the gantry sag in Cartesian coordinates. ax (None, matplotlib.axes) The axis to plot to. If None, creates a new plot. show (bool) Whether to show the image. plot_axis_images(axis= Gantry, show=true, ax=none) Plot all CAX/BB positions for the images of a given axis. For example, axis= Couch plots a reference image, and all the BB points of the other images where the couch was moving. axis ({ Gantry, Collimator, Couch, Combo }) The images/markers from which accelerator axis to plot. show (bool) Whether to actually show the images. ax (None, matplotlib.axes) The axis to plot to. If None, creates a new plot. plot_images(show=true) Plot a grid of all the images acquired. Four columns are plotted with the titles showing which axis that column represents. show (bool) Whether to show the image. save_images(filename, **kwargs) Save the figure of plot_images() to file. Keyword arguments are passed to matplotlib.pyplot.savefig(). filename (str) The name of the file to save to. plot_summary(show=true) Plot a summary figure showing the gantry sag and wobble plots of the three axes. 9.7. API Documentation 91

save_summary(filename, **kwargs) Save the summary image. results() Return the analysis results summary. class pylinac.winston_lutz.imagemanager(directory) Bases: list Manages the images of a Winston-Lutz test. directory (str) The path to the images. class pylinac.winston_lutz.wlimage(file) Bases: pylinac.core.image.dicomimage Holds individual Winston-Lutz EPID images, image properties, and automatically finds the field CAX and BB. file (str) Path to the image file. gantry_angle Gantry angle of the irradiation. collimator_angle Collimator angle of the irradiation. couch_angle Couch angle of the irradiation. y_offset The offset or distance between the field CAX and BB in the y-direction (AP). x_offset The offset or distance between the field CAX and BB in the x-direction (LR). z_offset The offset or distance between the field CAX and BB in z-direction (SI). cax_line_projection The projection of the field CAX through space around the area of the BB. Used for determining gantry isocenter size. Returns The virtual line in space made by the beam CAX. Return type Line cax2bb_vector The vector in mm from the CAX to the BB. cax2bb_distance The scalar distance in mm from the CAX to the BB. plot(ax=none, show=true, clear_fig=false) Plot the image, zoomed-in on the radiation field, along with the detected BB location and field CAX location. ax (None, matplotlib Axes instance) The axis to plot to. If None, will create a new figure. show (bool) Whether to actually show the image. clear_fig (bool) Whether to clear the figure first before drawing. 92 Chapter 9. Winston-Lutz module documentation

save_plot(filename, **kwargs) Save the image plot to file. variable_axis The axis that is varying. There are five types of images: Reference : All axes are at 0. Gantry: All axes but gantry at 0. Collimator : All axes but collimator at 0. Couch : All axes but couch at 0. Combo : More than one axis is not at 0. 9.7. API Documentation 93

94 Chapter 9. Winston-Lutz module documentation

CHAPTER 10 Planar Imaging module documentation 10.1 Overview The planar imaging module analyzes phantom images taken with the kv or MV imager in 2D; for example, the Leeds and PipsPro phantoms. Features: 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. 10.2 Leeds Phantom The Leeds phantom is used to measure image quality metrics for the kv imager of a linac. It contains both high and low contrast ROIs. 10.2.1 Running the Leeds Demo To run the Leeds TOR demo, create a script or start an interpreter session and input: from pylinac import LeedsTOR LeedsTOR.run_demo() A figure showing the phantom, low contrast plot, and RMTF will be generated: 95

10.2.2 Image Acquisition You can acquire the images any way you like. Just ensure that the phantom is not touching a field edge. It is also recommended by the manufacturer to rotate the phantom to a non-cardinal angle so that pixel aliasing does not occur for the high-contrast line pairs. 10.2.3 Typical Use Import the class: from pylinac import LeedsTOR The minimum needed to get going is to: Load image Load the planar image as you would any other class: by passing the path directly to the constructor: leeds = LeedsTOR('my/leeds.dcm') Alternatively, a URL can be passed: leeds = LeedsTOR.from_url('http://myserver.com/leeds') You may also use the demo image: leeds = LeedsTOR.from_demo_image() Analyze the images Analyze the image using the analyze() method. The low and high contrast thresholds can be specified: leeds.analyze(low_contrast_threshold=0.01, hi_contrast_threshold=0.5) View the results The results of analysis can be viewed with the plot_analyzed_image() method. Note that each subimage can be turned on or off.: # don't show the low contrast plot leeds.plot_analyzed_image(lowcontrast=false) 96 Chapter 10. Planar Imaging module documentation

The figure can also be saved: leeds.save_analyzed_image('myprofile.png') 10.2.4 Algorithm Leeds phantom analysis is straightforward: find the phantom in the image, then sample ROIs at the appropriate locations. The algorithm works like such: Allowances The images can be acquired at any SID. The images can be acquired with any size kv imager. The phantom can be at any distance. The phantom can be at any angle. The phantom can be flipped either way. Restrictions Warning: Analysis can fail or give unreliable results if any Restriction is violated. The phantom must not be touching or close to any image edges. Pre-Analysis Determine phantom location The Leeds phantom is found by performing a canny edge detection algorithm to the image. The thin structures found are sifted by finding appropriately-sized ROIs. This may include the outer phantom edge and the metal ring just inside. The average central position of the circular ROIs is set as the phantom center. Determine phantom angle To find the rotational angle of the phantom, a similar process is employed, but square-like features are searched for in the edge detection image. Because there are two square areas, the ROI 10.2. Leeds Phantom 97

with the highest attenuation (lead) is chosen. The angle between the phantom center and the lead square center is set as the angle. Determine rotation direction The phantom might be placed upside down. To keep analysis consistent, a circular profile is sampled at the radius of the low contrast ROIs starting at the lead square. Peaks are searched for on each semicircle. The side with the most peaks is the side with the higher contrast ROIs. Analysis is always done counter-clockwise. If the ROIs happen to be clockwise, the image is flipped left-right and angle/center inverted. Analysis Calculate low contrast Because the phantom center and angle are known, the angles to the ROIs can also be known. For each contrast ROI, both it and a background ROI are sampled. From here, the contrast can be known: Contrast ROI = ROI val ROI background ROI val +ROI background. Calculate high contrast Again, because the phantom position and angle are known, offsets are applied to sample the high contrast line pair regions. For each sample, the relative MTF is calculated: MT F ROI = ROI max ROI min ROI max+roi min. Post-Analysis Determine passing low and high contrast ROIs For each low and high contrast region, the determined value is compared to the threshold. The plot colors correspond to the pass/fail status. 10.2.5 Troubleshooting If you re having trouble getting the Leeds phantom analysis to work, first check out the Troubleshooting section. If the issue is not listed there, then it may be one of the issues below. The most common reason for failing is having the phantom near an image edge. The resulting error is usually that the phantom angle cannot be determined. For example, this image would throw an error: 98 Chapter 10. Planar Imaging module documentation

The below image also fails. Technically, the phantom is in the image, but the top blade skews the pixel values such that the phantom edge cannot be properly found at the top. This fails to identify the true phantom edge, causing the angle to also not be found: 10.2. Leeds Phantom 99

Another problem is that the image may have a non-uniform background. This can cause pylinac s automatic inversion correction to incorrectly invert the image. For example, this image falsely inverts: 100 Chapter 10. Planar Imaging module documentation

When analyzed, the angle is 180 degrees opposite the lead square, causing the ROIs to be flipped 180 degrees. To correct this problem, pass invert=true to analyze(). This will force pylinac to invert the image the opposite way and correctly identify the lead square. 10.3 PipsPro Phantom The PipsPro phantom is an MV imaging quality assurance phantom and has high and low contrast regions, just as the Leeds phantom, but with different geometric configurations. 10.3.1 Running the Leeds Demo To run the PipsPro demo, create a script or start an interpreter session and input: from pylinac import PipsPro PipsPro.run_demo() A figure showing the phantom, low contrast plot, and RMTF will be generated: 10.3. PipsPro Phantom 101

10.3.2 Image Acquisition The PipsPro phantom has a specific setup as recommended by the manufacturer. The phantom should be angled 45 degrees, with the 1 pointed toward the gantry stand and centered along the CAX. For best results when using pylinac, open the jaws to fully cover the EPID. 10.3.3 Typical Use Import the class: from pylinac import PipsPro The minimum needed to get going is to: Load image Load the planar image as you would any other class: by passing the path directly to the constructor: pp = PipsPro('path/to/pipspro.dcm') Alternatively, a URL can be passed: pp = PipsPro.from_url('http://myserver.com/pipspro') You may also use the demo image: pp = PipsPro.from_demo_image() Analyze the images Analyze the image using the analyze() method. The low and high contrast thresholds can be specified: pp.analyze(low_contrast_threshold=0.01, hi_contrast_threshold=0.5) View the results The results of analysis can be viewed with the plot_analyzed_image() method. Note that each subimage can be turned on or off.: # don't show the low contrast plot pp.plot_analyzed_image(lowcontrast=false) 102 Chapter 10. Planar Imaging module documentation

The figure can also be saved: pp.save_analyzed_image('myprofile.png') 10.3.4 Algorithm The algorithm works like such: Allowances The images can be acquired at any SID. The phantom can be at any distance. The images can be acquired with any EPID. The phantom can be somewhat offset from the ideal 45 degree orientation. Restrictions Warning: Analysis can fail or give unreliable results if any Restriction is violated. The phantom must not be touching any image edges. The phantom should have the 1 pointing toward the gantry stand. Pre-Analysis Determine phantom location A canny edge search is performed on the image. Connected edges that are semi-round and angled are thought to possibly be the phantom. Of the ROIs, the one with the longest axis is said to be the phantom edge. The center of the bounding box of the ROI is set as the phantom center. Determine phantom radius and angle The major axis length of the ROI determined above serves as the phantom radius. The orientation of the edge ROI serves as the phantom angle. Analysis Calculate low contrast Because the phantom center and angle are known, the angles to the ROIs can also be known. For each contrast ROI, both it and a background ROI are sampled. From here, the contrast can be known: Contrast ROI = ROI val ROI background ROI val +ROI background. 10.3. PipsPro Phantom 103

Calculate high contrast Again, because the phantom position and angle are known, offsets are applied to sample the high contrast line pair regions. For each sample, the relative MTF is calculated: MT F ROI = ROI max ROI min ROI max+roi min. Post-Analysis Determine passing low and high contrast ROIs For each low and high contrast region, the determined value is compared to the threshold. The plot colors correspond to the pass/fail status. 10.3.5 Troubleshooting If you re having issues with the PipsPro class, make sure you have correctly positioned the phantom as per the manufacturer s instructions (also see Image Acquisition). One issue that may arise is incorrect inversion. If the jaws are closed tightly around the phantom, the automatic inversion correction may falsely invert the image, just as for the Leeds phantom. If you have an image like the one below, add invert=true to analyze(). images/closed_pipspro.jpg 10.4 API Documentation class pylinac.planar_imaging.leedstor(filepath) Bases: pylinac.planar_imaging.imagephantombase Class that analyzes Leeds TOR phantom planar kv images for kv QA. lc_rois list LowContrastDiskROI instances of the low contrast ROIs. lc_ref_rois list LowContrastDiskROI instances of the low contrast reference ROIs, which are placed just inside each contrast ROI. hc_rois list HighContrastDiskROI instances of the high contrast line pair regions. hc_ref_rois list HighContrastDiskROI instances of the 2 solid areas beside the high contrast line pair regions, which determine the normalized MTF value. filepath (str) Path to the image file. static run_demo() Run the Leeds TOR phantom analysis demonstration. 104 Chapter 10. Planar Imaging module documentation

analyze(low_contrast_threshold=0.005, hi_contrast_threshold=0.4, invert=false) Analyze the image. low_contrast_threshold (float) The threshold for the low-contrast bubbles to be seen. hi_contrast_threshold (float) The threshold percentage that the relative MTF must be above to be seen. Must be between 0 and 1. invert (bool) Whether to force an inversion of the image. Pylinac tries to infer the correct inversion but uneven backgrounds can cause this analysis to fail. If the contrasts/mtf ROIs appear correctly located but the plots are wonky, try setting this to True. plot_analyzed_image(image=true, low_contrast=true, high_contrast=true, show=true) Plot the analyzed image, which includes the original image with ROIs marked, low-contrast plots and high-contrast plots. image (bool) Show the image. low_contrast (bool) Show the low contrast values plot. high_contrast (bool) Show the high contrast values plot. show (bool) Whether to actually show the image when called. from_demo_image() Instantiate and load the demo image. from_url(url) url (str) The URL to the image. save_analyzed_image(filename, **kwargs) Save the analyzed image to a file. filename (str) The location and filename to save to. kwargs Keyword arguments are passed to plt.savefig(). class pylinac.planar_imaging.pipsproqc3(filepath) Bases: pylinac.planar_imaging.imagephantombase Class for analyzing high and low contrast of the PipsPro QC-3 MV phantom. lc_rois list LowContrastDiskROI instances of the low contrast ROIs, other than the reference ROI (below). lc_ref_rois list hc_rois list LowContrastDiskROI instance of the low contrast reference ROI (15mm PVC). HighContrastDiskROI instances of the high contrast line pair regions. filepath (str) Path to the image file. 10.4. API Documentation 105

static run_demo() Run the PipsPro QC-3 phantom analysis demonstration. analyze(low_contrast_threshold=0.005, hi_contrast_threshold=0.5, invert=false) Analyze the PipsPro phantom. low_contrast_threshold (float) The threshold for the low-contrast bubbles to be seen. hi_contrast_threshold (float) The threshold percentage that the relative MTF must be above to be seen. Must be between 0 and 1. invert (bool) Whether to force an inversion of the image. Pylinac tries to infer the correct inversion but uneven backgrounds can cause this analysis to fail. If the contrasts/mtf ROIs appear correctly located but the plots are wonky, try setting this to True. plot_analyzed_image(image=true, low_contrast=true, high_contrast=true, show=true) Plot the analyzed image. image (bool) Show the image. low_contrast (bool) Show the low contrast values plot. high_contrast (bool) Show the high contrast values plot. show (bool) Whether to actually show the image when called. from_demo_image() Instantiate and load the demo image. from_url(url) url (str) The URL to the image. save_analyzed_image(filename, **kwargs) Save the analyzed image to a file. filename (str) The location and filename to save to. kwargs Keyword arguments are passed to plt.savefig(). 106 Chapter 10. Planar Imaging module documentation

CHAPTER 11 Flatness/Symmetry module documentation 11.1 Overview The Flatness & Symmetry module (flatsym) allows a physicist to check their linac s beam profile against wellknown flatness/symmetry calculation standards, reporting absolute values. Film or EPID images can be loaded in and analyzed. 11.2 Running the Demo To run the demo of the flatsym module, import the main class from it and run the demo method: from pylinac.flatsym import BeamImage my_img = BeamImage() my_img.run_demo() Which will result in the following plot: 107

11.3 Typical Use In most instances, a physicist is interested in quickly calculating the flatness, symmetry, or both of the image in question. The flatsym module allows you to do this easily, using any of multiple definitions of flatness or symmetry. To get started, import the BeamImage class from the flatsym module: from pylinac.flatsym import BeamImage Loading images is easy and just like any other module: # from a file my_file = r"c:/my/qa/folder/img.dcm" my_img = BeamImage(my_file) # or using a UI my_img = BeamImage.open_UI() You can calculate the flatness or symmetry directly, e.g.: my_img.flatness() [1.91, 2.6] # or whatever the values are. Returns: [crossplane, inplane] my_img.symmetry() [3.08, 2.3] Any physicist worth their salt will want to check that the right profile has been taken however. This is easily done with similar methods: my_img.plot_flatness() Default behavior is to analyze both planes at the calculated center of the field, but other options exist: Individual planes can be analyzed instead of both. The position of the profile can be directly passed. Multiple Analysis Definitions exist. See also: plot_flatsym() parameters for more details. 108 Chapter 11. Flatness/Symmetry module documentation

11.4 Analysis Definitions Warning: The following definitions are for photons only. There are multiple definitions for both flatness and symmetry. Your machine vendor uses certain equations, or your clinic may use a specific definition. Pylinac has a number of built-in definitions which you can use. Symmetry: Name, Vendors that use it Equation Point Difference, Varian 100 * max( L pt R pt )/D CAX over 80%FW, where L pt and R pt are equidistant from the CAX. Point Difference Quotient (IEC), Elekta 100 * max( L pt /R pt, R pt /L pt ) over 80%FW if 10<FW<30cm 1. Flatness: Name, Vendors that use it Equation Variation over mean (80%), Varian 100 * D max D min /(D max + D min ) within 80%FW. Dmax/Dmin (IEC), Elekta 100 * D max /D min within 80%FW for 10<FW<30cm 1. Note: Siemens and other definitions (e.g. Area, Area/2) will be added if the community asks for it. 11.5 Algorithm There is little of a true algorithm in flatsym other than automatic field determination. Thus, this section is more terminology and notekeeping. Allowances The image can be any size. The image can be digitized film or EPID (most image formats and DICOM). The image can be either inversion (Radiation is dark or light). Restrictions The module is only meant for photon analysis at the moment (there are sometimes different equations for electrons for the same definition name). The image should be near perpendicular/normal to the image edge; this actually won t cause the module to fail, but may invalidate the results accuracy. Accounting for angle may come in a future version if the community asks for it. Analysis Determine the profile position - If the profile position determination is left to the automatic analysis, the row and column image sums are analyzed for the center of the FWHM. These then determine the location, either crossplane, inplane, or both. If the values are explicitly passed in, these values are used directly (or converted from a fraction). 1 The region calculated over actually varies by the following: for 5<FW<10cm, FW - 2*1cm; for 10<FW<30cm, FW - 2*0.1*FW (i.e. 80%FW); for 30cm<FW, FW - 2*6cm. Pylinac currently only uses the 80%FW no matter the FW, but accounting for the FW will come in a future version. 11.4. Analysis Definitions 109

Extract profiles - With the positions known, profile(s) are extracted and analyzed according to the method specified (see Analysis Definitions). For symmetry calculations that operate around the CAX, the CAX must first be determined, which is the center of the FWHM of the profile. 11.6 API Documentation class pylinac.flatsym.beamimage(filepath=none) Class for analyzing flatness & symmetry of a 2D beam image (perpendicular to the beam). filepath (None, str) If None, image must be loaded later. If a str, path to the image file. load_demo_image() Load the demo image. run_demo(plane= both, position= auto, method= varian ) Run the BeamImage flatness & symmetry demo. See also: plot_flatsym() for plane, position, and method parameter info. plot_flatsym(plane= both, position= auto, method= varian ) Plot both the flatness and symmetry. plane (str) The plane of interest, either crossplane, inplane, or both. Shortcut descriptions are also allowed, e.g. x == cross == crossplane and i == in == inplane. position (str, 2-element sequence) If the position is a str, auto, the position will be determined automatically, at the center of the FWHM. If the position is a tuple/list/array it must be 2 elements long, specifying the location desired in (y, x) to take the flatness/symmetry over. The elements may be either integers, specifying the actual pixel values, or floats <1.0, specifying the image fraction (e.g. 0.4 will result in the pixel at 40% distance along an axis). Combinations of int/float is also allowed. See Examples section for more. method (str) The method of analysis. There are multiple methods, specified by their real name as well as the vendor that utilizes the given method. For example, Varian uses the variation of the mean in the 80% field width for flatness. The method could be specified by using varian, or by VoM80. Another example is Elekta, who use the Point Difference Quotient-IEC definition for symmetry. Thus, one could use elekta or pdq-iec. Method names are case insensitive. For flatness, Varian, VoM80, Siemens all perform the same calculation. Elekta and IEC both perform the same calculation. For symmetry, Varian, Point Difference both perform the same calculation. Elekta and PDQ-IEC both perform the same calculation. See Analysis Definitions for equations. 110 Chapter 11. Flatness/Symmetry module documentation

Examples >>> bi = BeamImage() >>> bi.load_demo_image() Defaults: >>> bi.plot_flatsym() Specify a single plane and different method: >>> bi.plot_flatsym(plane='x', method='elekta') Specify a custom position: >>> bi.plot_flatsym(position=(300, 0.6)) # int/float combos allowed symmetry(plane= both, position= auto, method= varian ) Determine and return the symmetry of the image. See also: plot_flatsym() for plane, position, and method parameter info. plot_symmetry(plane= both, position= auto, method= varian, plot_mirror=true, show=true, ax=none) Plot the profile, highlighting symmetry. See also: show_mirror (bool) If True (default), shows the mirrored profile, making visual comparison easier. ax (None, matplotlib.axes, list containing matplotlib.axes) If None, the plot will be created on a new figure/axes, otherwise it will be plotted to the passed axes. show (bool) If True (default), the plot will be drawn/shown at the end of the method call. Not showing the plot is useful when plotting multiple flat/sym plots. plot_flatsym() for plane, position, and method parameter info. flatness(plane= crossplane, position= auto, method= varian ) Determine the flatness of the image. See also: plot_flatsym() for plane, position, and method parameter info. plot_flatness(plane= both, position= auto, method= varian, ax=none, show=true) Plot the profile showing the min and max points. See also: ax (None, matplotlib.axes, list of matplotlib.axes) If None, the plot will be created on a new figure/axes, otherwise it will be plotted to the passed axes. show (bool) If True (default), the plot will be drawn/shown at the end of the method call. Not showing the plot is useful when plotting multiple flat/sym plots. plot_flatsym() for plane, position, and method parameter info. 11.6. API Documentation 111

112 Chapter 11. Flatness/Symmetry module documentation

CHAPTER 12 Directory Watching As of version 0.8, you can now set up a script to run that watches a directory for incoming files and analyzes them with pylinac if certain keywords are in the file. 12.1 Setup Note: To use the watcher script, watchdog must be installed. To use the watcher script, locate the pylinac package on the computer. Then, open up a terminal and run the watcher.py file: > python "C:\path\to\pylinac\watcher.py" This will watch the directory the watcher script is in. To watch a different directory, pass it as an argument: > python "C:\path\to\pylinac\watcher.py" "C:\path\to\watch" A logger will notify when the script has started, when a file gets added, and what the analysis status is. If a file gets analyzed successfully, a.png and.txt file with the same name as the original file will be generated in the directory. 12.2 Tweaking The watcher script runs using default values for keywords and tolerance. To adjust these values, change the keywords and args attributes. See the API docs. The defaults are as follows: Note: Only.zip files are accepted for CBCT and VMAT analysis. Only.bin or.dlg files are acceptable for machine logs. Starshots - keywords: star ; radius: 0.8; tolerance: 1 Picket Fences - keywords: pf, picket ; tolerance: 0.5mm; action tolerance: 0.3mm CBCTs - keywords: ct, cbct ; HU tolerance: 40HUs; scaling tolerance: 1mm VMATs - keywords: vmat, drgs, drmlc ; tolerance: 1.5% 113

Logs - keywords: None; resolution: 0.1mm; distance to agreement: 1mm; dose to agreement: 1%; threshold: 10% 12.3 API Documentation class pylinac.watcher.analyzemixin(path) Bases: object Mixin for processing files caught by the pylinac watcher. obj class The class that analyzes the file; e.g. Starshot, PicketFence, etc. keywords iterable args dict Holds the keywords that are looked for in the file name. If the filename has that keyword, analysis will be attempted on that file. Dictionary that holds the tolerance settings of analysis. obj alias of object img_filename The name of the file for the analyzed image. txt_filename The name of the file for the text results. save_image() Save the analyzed image to file. save_text() Save the analysis results to a text file. class pylinac.watcher.analyzestar(path) Bases: pylinac.watcher.analyzemixin Analysis class for starshots. obj alias of Starshot class pylinac.watcher.analyzepf(path) Bases: pylinac.watcher.analyzemixin Analysis class for picket fences. obj alias of PicketFence class pylinac.watcher.analyzecbct(path) Bases: pylinac.watcher.analyzemixin Analysis class for CBCTs. 114 Chapter 12. Directory Watching

obj(zip_file) Construct a CBCT object and pass the zip file. New in version 0.6. class pylinac.watcher.analyzevmat(path) Bases: pylinac.watcher.analyzemixin Analysis class for VMATs. obj(path) Load VMAT images from a ZIP file that contains both images. New in version 0.8. class pylinac.watcher.analyzelog(path) Bases: pylinac.watcher.analyzemixin Analysis class for dynalogs or trajectory logs. obj alias of MachineLog 12.3. API Documentation 115

116 Chapter 12. Directory Watching

CHAPTER 13 Core Modules Documentation The following is the API documentation for the core modules of pylinac. These can be used directly, or as the base for mixin classes or methods. 13.1 Image Module This module holds classes for image loading and manipulation. class pylinac.core.image.image Bases: object A swiss-army knife, delegate class for loading in images and image-like things. The class should not be instantiated directly, but through its class methods. These methods return not an Image class but one of three specialized image classes: DicomImage : Handles all DICOM images; utilizes pydicom. FileImage : Handles JPEG, BMP, TIF, and other regular image files; utilizes Pillow. ArrayImage : Handles 2D numpy arrays; convenient for doing processing of arrays that represent an image. There are two methods to construct these classes: load() : For loading single images/arrays. load_multiples() : For loading and superimposing multiple images/arrays. All the images must be the same size. Examples Load an image from a file: >>> my_image = "C:\QA\image.tif" >>> img = Image.load(my_image) # returns a FileImage >>> img.filter(5) Loading from an array is just like loading from a file: >>> arr = np.arange(36).reshape(6, 6) >>> img = Image.load(arr) # returns an ArrayImage 117

Load multiple images: >>> paths = ['starshot1.tif', 'starshot2.tif'] >>> superimposed_img = Image.load_multiples(paths) classmethod load(path, **kwargs) Load a DICOM image, JPG/TIF/BMP image, or numpy 2D array. path (str, file-object) The path to the image file or data stream or array. kwargs See FileImage, DicomImage, or ArrayImage for keyword arguments. Returns Return type depends on input image. Return type :FileImage, ArrayImage, or DicomImage classmethod load_url(url, **kwargs) Load an image from a URL. url (str) A string pointing to a valid URL that points to a file. Note: For some images (e.g. Github), the raw binary URL must be used, not simply the basic link. classmethod load_multiples(image_file_list, method= mean, stretch=true, **kwargs) Combine multiple image files into one superimposed image. New in version 0.5.1. class pylinac.core.image.imagemixin(path) Bases: object Base class for the Image classes. array numpy.ndarray The actual image pixel array. center geometry.point The center pixel of the image as a Point. center Return the center position of the image array as a Point. plot(ax=none, show=true, clear_fig=false) Plot the image. filter(size=0.05, kind= median ) Filter the profile. size (int, float) Size of the median filter to apply. If a float, the size is the ratio of the length. Must be in the range 0-1. E.g. if size=0.1 for a 1000-element array, the filter will be 100 elements. If an int, the filter is the size passed. kind ({ median, gaussian }) The kind of filter to apply. If gaussian, size is the sigma value. 118 Chapter 13. Core Modules Documentation

remove_edges(pixels=15) Removes pixels on all edges of the image. pixels (int) Number of pixels to cut off all sides of the image. invert() Invert (imcomplement) the image. rot90(n=1) Wrapper for numpy.rot90. resize(size, interp= bilinear ) Resize/scale the image. Wrapper for scipy s imresize: threshold(threshold, kind= high ) Apply a threshold filter. threshold (int) The cutoff value. kind (str) If high (default), will apply a high-pass threshold. All values above the cutoff are left as-is. Remaining points are set to 0. If low, will apply a low-pass threshold. as_binary(threshold) Return a binary (black & white) image based on the given threshold. threshold (int, float) The threshold value. If the value is above or equal to the threshold it is set to 1, otherwise to 0. Returns Return type ArrayImage dist2edge_min(point) Calculates minimum distance from given point to image edges. point (geometry.point, tuple) Returns Return type float ground() Ground the profile such that the lowest value is 0. Note: This will also ground profiles that are negative or partially-negative. For such profiles, be careful that this is the behavior you desire. normalize(norm_val= max ) Normalize the image values to the given value. norm_val (str, number) If a string, must be max, which normalizes the values to the maximum value. If a number, normalizes all values to that number. check_inversion(box_size=20, offset=10) Check the image for inversion by sampling the 4 image corners. If the average value of the four corners is above the average pixel value, then it is very likely inverted. gamma(comparison_image, doseta=1, distta=1, threshold=0.1) Calculate the gamma between the current image (reference) and a comparison image. 13.1. Image Module 119

New in version 1.2. The gamma calculation is based on Bakai et al eq.6, which is a quicker alternative to the standard Low gamma equation. comparison_image The comparison image. Must be a ArrayImage, DicomImage, or FileImage. The image must have the same DPI/DPMM to be comparable. The size of the images must also be the same. doseta (int, float) Dose-to-agreement in percent; e.g. 2 is 2%. distta (int, float) Distance-to-agreement in mm. threshold (float) The dose threshold percentage of the maximum dose, below which is not analyzed. Must be between 0 and 1. Returns gamma_map The calculated gamma map. Return type numpy.ndarray class pylinac.core.image.dicomimage(path, *, dtype=none) Bases: pylinac.core.image.imagemixin An image from a DICOM RTImage file. metadata pydicom Dataset The dataset of the file as returned by pydicom without pixel data. path (str, file-object) The path to the file or the data stream. dtype (dtype, None, optional) The data type to cast the image data as. If None, will use whatever raw image format is. sid The Source-to-Image in mm. dpi The dots-per-inch of the image, defined at isocenter. dpmm The Dots-per-mm of the image, defined at isocenter. E.g. if an EPID image is taken at 150cm SID, the dpmm will scale back to 100cm. cax The position of the beam central axis. If no DICOM translation tags are found then the center is returned. class pylinac.core.image.fileimage(path, *, dpi=none, sid=1000) Bases: pylinac.core.image.imagemixin An image from a regular file (.tif,.jpg,.bmp). info dict sid float The info dictionary as generated by Pillow. 120 Chapter 13. Core Modules Documentation

The SID value as passed in upon construction. path (str, file-object) The path to the file or a data stream. dpi (int, float) The dots-per-inch of the image, defined at isocenter. Note: If a DPI tag is found in the image, that value will override the parameter, otherwise this one will be used. sid (int, float) The Source-to-Image distance in mm. dpi The dots-per-inch of the image, defined at isocenter. dpmm The Dots-per-mm of the image, defined at isocenter. E.g. if an EPID image is taken at 150cm SID, the dpmm will scale back to 100cm. class pylinac.core.image.arrayimage(array, *, dpi=none, sid=1000, dtype=none) Bases: pylinac.core.image.imagemixin An image constructed solely from a numpy array. array (numpy.ndarray) The image array. dpi (int, float) The dots-per-inch of the image, defined at isocenter. Note: If a DPI tag is found in the image, that value will override the parameter, otherwise this one will be used. sid (int, float) The Source-to-Image distance in mm. dtype (dtype, None, optional) The data type to cast the image data as. If None, will use whatever raw image format is. dpmm The Dots-per-mm of the image, defined at isocenter. E.g. if an EPID image is taken at 150cm SID, the dpmm will scale back to 100cm. dpi The dots-per-inch of the image, defined at isocenter. class pylinac.core.image.dicomimagestack(folder=none, dtype=none) Bases: object A class that loads and holds a stack of DICOM images (e.g. a CT dataset). The class can take a folder or zip file and will read CT images. The images must all be the same size. Supports indexing to individual images. images list Holds instances of DicomImage. Can be accessed via index; i.e. self[0] == self.images[0]. Load a folder with DICOM CT images. 13.1. Image Module 121

folder (str) Path to the folder. dtype (dtype, None, optional) The data type to cast the image data as. If None, will use whatever raw image format is. classmethod from_zip(zip_path, dtype=none) Load a DICOM ZIP archive. zip_path (str) Path to the ZIP archive. dtype (dtype, None, optional) The data type to cast the image data as. If None, will use whatever raw image format is. plot(slice=0) Plot a slice of the DICOM dataset. metadata The metadata of the first image; shortcut attribute. Only attributes that are common throughout the stack should be used, otherwise the individual image metadata should be used. 13.2 Geometry Module Module for classes that represent common geometric objects or patterns. pylinac.core.geometry.tan(degrees) Calculate the tangent of the given degrees. pylinac.core.geometry.cos(degrees) Calculate the cosine of the given degrees. pylinac.core.geometry.sin(degrees) Calculate the sine of the given degrees. class pylinac.core.geometry.vector(x=0, y=0, z=0) Bases: object A vector with x, y, and z coordinates. as_scalar() Return the scalar equivalent of the vector. distance_to(thing) Calculate the distance to the given point. thing (Circle, Point, 2 element iterable) The other point to calculate distance to. pylinac.core.geometry.vector_is_close(vector1, vector2, delta=0.1) Determine if two vectors are with delta of each other; this is a simple coordinate comparison check. class pylinac.core.geometry.point(x=0, y=0, z=0, idx=none, value=none, as_int=false) Bases: object A geometric point with x, y, and z coordinates/attributes. x (number-like, Point, iterable) x-coordinate or iterable type containing all coordinates. If iterable, values are assumed to be in order: (x,y,z). y (number-like, optional) y-coordinate 122 Chapter 13. Core Modules Documentation

idx (int, optional) Index of point. Useful for sequential coordinates; e.g. a point on a circle profile is sometimes easier to describe in terms of its index rather than x,y coords. value (number-like, optional) value at point location (e.g. pixel value of an image) as_int (boolean) If True, coordinates are converted to integers. distance_to(thing) Calculate the distance to the given point. thing (Circle, Point, 2 element iterable) The other thing to calculate distance to. as_array(only_coords=true) Return the point as a numpy array. class pylinac.core.geometry.circle(center_point=none, radius=none) Bases: object A geometric circle with center Point, radius, and diameter. center_point (Point, optional) Center point of the wobble circle. radius (float, optional) Radius of the wobble circle. diameter Get the diameter of the circle. plot2axes(axes, edgecolor= black, fill=false) Plot the Circle on the axes. axes (matplotlib.axes.axes) An MPL axes to plot to. edgecolor (str) The color of the circle. fill (bool) Whether to fill the circle with color or leave hollow. class pylinac.core.geometry.line(point1, point2) Bases: object A line that is represented by two points or by m*x+b. Notes Calculations of slope, etc are from here: http://en.wikipedia.org/wiki/linear_equation and here: http://www.mathsisfun.com/algebra/line-equation-2points.html m point1 (Point, optional) One point of the line point2 (Point, optional) Second point along the line. Return the slope of the line. m = (y1 - y2)/(x1 - x2) From: http://www.purplemath.com/modules/slope.htm 13.2. Geometry Module 123

b Return the y-intercept of the line. b = y - m*x y(x) Return y-value along line at position x. x(y) Return x-value along line at position y. center Return the center of the line as a Point. length Return length of the line, if finite. distance_to(point) Calculate the minimum distance from the line to a point. Equations are from here: http://mathworld.wolfram.com/point-linedistance2-dimensional.html #14 point (Point, iterable) The point to calculate distance to. plot2axes(axes, width=1, color= w ) Plot the line to an axes. axes (matplotlib.axes.axes) An MPL axes to plot to. color (str) The color of the line. class pylinac.core.geometry.rectangle(width, height, center, as_int=false) Bases: object A rectangle with width, height, center Point, top-left corner Point, and bottom-left corner Point. width (number) Width of the rectangle. height (number) Height of the rectangle. center (Point, iterable, optional) Center point of rectangle. as_int (bool) If False (default), inputs are left as-is. If True, all inputs are converted to integers. plot2axes(axes, edgecolor= black, angle=0.0, fill=false, alpha=1, facecolor= g ) Plot the Rectangle to the axes. axes (matplotlib.axes.axes) An MPL axes to plot to. edgecolor (str) The color of the circle. angle (float) Angle of the rectangle. fill (bool) Whether to fill the rectangle with color or leave hollow. 13.3 Profile Module Module of objects that resemble or contain a profile, i.e. a 1 or 2-D f(x) representation. 124 Chapter 13. Core Modules Documentation

pylinac.core.profile.stretch(array, min=0, max=1) Stretch the profile to the min and max parameter values. min (number) The new minimum of the values max (number) The new maximum value. class pylinac.core.profile.profilemixin Bases: object A mixin to provide various manipulations of 1D profile data. invert() Invert (imcomplement) the profile. normalize(norm_val= max ) Normalize the profile to the given value. norm_val (str, number) If a string, must be max, which normalizes the values to the maximum value. If a number, normalizes all values to that number. stretch(min=0, max=1) Stretch the profile to the min and max parameter values. min (number) The new minimum of the values max (number) The new maximum value. ground() Ground the profile such that the lowest value is 0. Returns The minimum value that was used as the grounding value. Return type float filter(size=0.05, kind= median ) Filter the profile. size (int, float) Size of the median filter to apply. If a float, the size is the ratio of the length. Must be in the range 0-1. E.g. if size=0.1 for a 1000-element array, the filter will be 100 elements. If an int, the filter is the size passed. kind ({ median, gaussian }) The kind of filter to apply. If gaussian, size is the sigma value. class pylinac.core.profile.singleprofile(values, normalize_sides=true, initial_peak=none) Bases: pylinac.core.profile.profilemixin A profile that has one large signal, e.g. a radiation beam profile. Signal analysis methods are given, mostly based on FWXM calculations. Profiles with multiple peaks are better suited by the MultiProfile class. values (array) The profile numpy array. Must be 1D. normalize_sides (bool, optional) If True (default), each side of the profile will be grounded independently. If False, the profile will be grounded by the profile global minimum. 13.3. Profile Module 125

values The profile array. initial_peak (int, optional) If the approximate peak of the profile is known it can be passed in. Not needed unless there is more than one major peak in the profile, e.g. a very high edge. fwxm(x=50, interpolate=false) Return the width at X-Max, where X is the percentage height. x (int) The percent height of the profile. E.g. x = 50 is 50% height, i.e. FWHM. interpolate (bool) If True, interpolates the values to give a more accurate FWXM. Returns The width in number of elements of the FWXM. Return type int, float fwxm_center(x=50, interpolate=false, kind= index ) Return the center index of the FWXM. See also: fwxm() penumbra_width(side= left, lower=20, upper=80, interpolate=false) Return the penumbra width of the profile. This is the standard penumbra width calculation that medical physics talks about in radiation profiles. Standard is the 80/20 width, although 90/10 is sometimes used. side ({ left, right, both }) Which side of the profile to determined penumbra. If both, the left and right sides are averaged. lower (int) The lower penumbra value used to calculate penumbra. Must be lower than upper. upper (int) The upper penumbra value used to calculate penumbra. interpolate (bool) Whether to interpolate the profile to get more accurate values. Raises ValueError If lower penumbra is larger than upper penumbra field_values(field_width=0.8) Return a subarray of the values of the profile for the given field width. This is helpful for doing, e.g., flatness or symmetry calculations, where you want to calculate something over the field, not the whole profile. field_width (float) The field width of the profile, based on the fwhm. Must be between 0 and 1. Returns Return type ndarray field_edges(field_width=0.8) Return the indices of the field width edges, based on the FWHM. See also: field_values() 126 Chapter 13. Core Modules Documentation

Returns Return type left_index, right_index field_calculation(field_width=0.8, calculation= mean ) Perform an operation on the field values of the profile. This function is useful for determining field symmetry and flatness. calculation ({ mean, median, max, min, area }) Calculation to perform on the field values. Returns See also: Return type float field_values() plot() Plot the profile. class pylinac.core.profile.multiprofile(values) Bases: pylinac.core.profile.profilemixin A class for analyzing 1-D profiles that contain multiple signals. Methods are mostly for finding & filtering the signals, peaks, valleys, etc. Profiles with a single peak (e.g. radiation beam profiles) are better suited by the SingleProfile class. values ndarray peaks list valleys list The array of values passed in on instantiation. List of Points, containing value and index information. Same as peaks, but for valleys. values (iterable) Array of profile values. plot(show_peaks=true) Plot the profile. show_peaks (bool) Whether to plot the peak locations as well. Will not show if a peak search has not yet been done. find_peaks(threshold=0.3, min_distance=0.05, max_number=none, search_region=(0.0, 1.0), kind= index ) Find the peaks of the profile using a simple maximum value search. This also sets the peaks attribute. threshold (int, float) The value the peak must be above to be considered a peak. This removes peaks that are in a low-value region. If passed an int, the actual value is the threshold. E.g. when passed 15, any peak less with a value <15 is removed. If passed a float, it will threshold as a percent. Must be between 0 and 1. E.g. when passed 0.4, any peak <40% of the maximum value will be removed. 13.3. Profile Module 127

min_distance (int, float) If passed an int, parameter is the number of elements apart a peak must be from neighboring peaks. If passed a float, must be between 0 and 1 and represents the ratio of the profile to exclude. E.g. if passed 0.05 with a 1000-element profile, the minimum peak width will be 0.05*1000 = 50 elements. max_number (int, None) Specify up to how many peaks will be returned. E.g. if 3 is passed in and 5 peaks are found, only the 3 largest peaks will be returned. If None, no limit will be applied. search_region (tuple of ints, floats, or both) The region within the profile to search. The tuple specifies the (left, right) edges to search. This allows exclusion of edges from the search. If a value is an int, it is taken as is. If a float, must be between 0 and 1 and is the ratio of the profile length. The left value must be less than the right. kind ({ value, index }) What kind of return is given. If index (default), returns the index of the point desired. If value, returns the value of the profile at the given index. Returns Either the values or indices of the peaks. Return type ndarray find_valleys(threshold=0.3, min_distance=0.05, max_number=none, search_region=(0.0, 1.0), kind= index ) Find the valleys (minimums) of the profile using a simple minimum value search. See also: Returns Either the values or indices of the peaks. Return type ndarray find_peaks() : Further parameter info. find_fwxm_peaks(x=50, threshold=0.3, min_distance=0.05, max_number=none, search_region=(0.0, 1.0), kind= index, interpolate=false, interpolation_factor=100, interpolation_type= linear ) Find peaks using the center of the FWXM (rather than by max value). See also: x (int, float) The Full-Width-X-Maximum desired. E.g. 0.7 will return the FW70%M. Values must be between 0 and 100. interpolate (bool) Whether to interpolate the profile to determine a more accurate peak location. interpolation_factor (int) The interpolation multiplication factor. E.g. if 10, the profile is interpolated to have 10x the number of values. Only used if interpolate is True. interpolated_type ({ linear, cubic }) The type of interpolation to perform. Only used if interpolate is True. find_peaks() Further parameter info subdivide(interpolation_factor=100, interpolation_type= linear ) Subdivide the profile data into SingleProfiles. Returns List of SingleProfile 128 Chapter 13. Core Modules Documentation

Return type list class pylinac.core.profile.circleprofile(center, radius, image_array, start_angle=0, ccw=true, sampling_ratio=1.0) Bases: pylinac.core.profile.multiprofile, pylinac.core.geometry.circle A profile in the shape of a circle. image_array ndarray The 2D image array. start_angle int, float ccw bool See also: Starting position of the profile in radians; 0 is right (0 on unit circle). How the profile is/was taken; clockwise or counter-clockwise. image_array (ndarray) The 2D image array. start_angle (int, float) Starting position of the profile in radians; 0 is right (0 on unit circle). ccw (bool) If True (default), the profile will proceed counter-clockwise (the direction on the unit circle). If False, will proceed clockwise. sampling_ratio (float) The ratio of pixel sampling to real pixels. E.g. if 1.0, the profile will have approximately the same number of elements as was encountered in the profile. A value of 2.0 will sample the profile at 2x the number of elements. Circle : Further parameter info. size The elemental size of the profile. x_locations The x-locations of the profile values. y_locations The x-locations of the profile values. find_peaks(threshold=0.3, min_distance=0.05, max_number=none, search_region=(0.0, 1.0), kind= index ) Overloads Profile to also map peak locations to the image. find_valleys(threshold=0.3, min_distance=0.05, max_number=none, search_region=(0.0, 1.0), kind= index ) Overload Profile to also map valley locations to the image. find_fwxm_peaks(x=50, threshold=0.3, min_distance=0.05, max_number=none, search_region=(0.0, 1.0), kind= index, interpolate=false, interpolation_factor=100, interpolation_type= linear ) Overloads Profile to also map the peak locations to the image. roll(amount) Roll the profile and x and y coordinates. 13.3. Profile Module 129

plot2axes(axes=none, edgecolor= black, fill=false, plot_peaks=true) Plot the circle to an axes. axes (matplotlib.axes, None) The axes to plot on. If None, will create a new figure of the image array. edgecolor (str) Color of the Circle; must be a valid matplotlib color. fill (bool) Whether to fill the circle. matplotlib keyword. plot_peaks (bool) If True, plots the found peaks as well. class pylinac.core.profile.collapsedcircleprofile(center, radius, image_array, start_angle=0, ccw=true, sampling_ratio=1.0, width_ratio=0.1, num_profiles=20) Bases: pylinac.core.profile.circleprofile A circular profile that samples a thick band around the nominal circle, rather than just a 1-pixel-wide profile to give a mean value. See also: width_ratio (float) The thickness of the band to sample. The ratio is relative to the radius. E.g. if the radius is 20 and the width_ratio is 0.2, the thickness will be 4 pixels. num_profiles (int) The number of profiles to sample in the band. Profiles are distributed evenly within the band. CircleProfile : Further parameter info. plot2axes(axes=none, edgecolor= black, fill=false, plot_peaks=true) Add 2 circles to the axes: one at the maximum and minimum radius of the ROI. See also: plot2axes() : Further parameter info. pylinac.core.profile.peak_detect(values, threshold=none, min_distance=10, max_number=none, search_region=(0.0, 1.0), find_min_instead=false) Find the peaks or valleys of a 1D signal. Uses the difference (np.diff) in signal to find peaks. Current limitations include: 1. Only for use in 1-D data; 2D may be possible with the gradient function. 2. Will not detect peaks at the very edge of array (i.e. 0 or -1 index) values (array-like) Signal values to search for peaks within. threshold (int, float) The value the peak must be above to be considered a peak. This removes peaks that are in a low-value region. If passed an int, the actual value is the threshold. E.g. when passed 15, any peak less with a value <15 is removed. If passed a float, it will threshold as a percent. Must be between 0 and 1. E.g. when passed 0.4, any peak <40% of the maximum value will be removed. min_distance (int, float) If passed an int, parameter is the number of elements apart a peak must be from neighboring peaks. If passed a float, must be between 0 and 1 130 Chapter 13. Core Modules Documentation

Returns and represents the ratio of the profile to exclude. E.g. if passed 0.05 with a 1000-element profile, the minimum peak width will be 0.05*1000 = 50 elements. max_number (int) Specify up to how many peaks will be returned. E.g. if 3 is passed in and 5 peaks are found, only the 3 largest peaks will be returned. find_min_instead (bool) If False (default), peaks will be returned. If True, valleys will be returned. max_vals (numpy.array) The values of the peaks found. max_idxs (numpy.array) The x-indices (locations) of the peaks. Raises ValueError If float not between 0 and 1 passed to threshold. 13.4 I/O Module I/O helper functions for pylinac. pylinac.core.io.load_zipfile(zfilename, read=false) Read a.zip file. zfilename (str) Path to the zip file. read (bool) Whether to read the zip files. Returns If read is False (default), returns a python zipfile.zipfile object. If read is True, returns an iterator of BytesIO objects. Return type files class pylinac.core.io.temporaryzipdirectory(zfile) Bases: tempfile.temporarydirectory Creates a temporary directory that unpacks a ZIP archive. class pylinac.core.io.temporaryzipurldirectory(url) Bases: pylinac.core.io.temporaryzipdirectory Creates a temporary directory that downloads & extracts a ZIP archive from a URL. pylinac.core.io.get_url(url, destination=none) Get the response from the URL. pylinac.core.io.is_valid_file(file_path, raise_error=true) Check if path points to a valid file. file_path (str) Path to the file. raise_error (boolean) If False (default), will simply return false. If True, will raise an error if path is not valid. Raises FileExistsError If file_path does not point to a valid file. pylinac.core.io.open_file(file, mode= rb ) Open a file if a file is a string, or open the object if file is a file object. If an already-opened file object, reset the pointer to 0. 13.4. I/O Module 131

pylinac.core.io.is_valid_dir(dir_path, raise_error=true) Check if path points to a valid directory. 13.5 ROI Module pylinac.core.roi.bbox_center(region) Return the center of the bounding box of an scikit-image region. region A scikit-image region as calculated by skimage.measure.regionprops(). Returns point Return type Point class pylinac.core.roi.diskroi(array, angle, roi_radius, dist_from_center, phantom_center) Bases: pylinac.core.geometry.circle An class representing a disk-shaped Region of Interest. array (ndarray) The 2D array representing the image the disk is on. angle (int, float) The angle of the ROI in degrees from the phantom center. roi_radius (int, float) The radius of the ROI from the center of the phantom. dist_from_center (int, float) The distance of the ROI from the phantom center. phantom_center (tuple) The location of the phantom center. pixel_value The median pixel value of the ROI. std The standard deviation of the pixel values. circle_mask() Return a mask of the image, only showing the circular ROI. class pylinac.core.roi.lowcontrastdiskroi(array, angle, roi_radius, dist_from_center, phantom_center, contrast_threshold, background=none) Bases: pylinac.core.roi.diskroi A class for analyzing the low-contrast disks. contrast_threshold (float, int) The threshold for considering a bubble to be seen. contrast_to_noise The contrast to noise ratio of the bubble: (Signal - Background)/Stdev. contrast The contrast of the bubble compared to background: (ROI - backg) / (ROI + backg). contrast_constant The contrast value times the bubble diameter. passed_constant Boolean specifying if ROI pixel value was within tolerance of the nominal value. 132 Chapter 13. Core Modules Documentation

plot_color Return one of two colors depending on if ROI passed. plot_color_constant Return one of two colors depending on if ROI passed. class pylinac.core.roi.highcontrastdiskroi(array, angle, roi_radius, dist_from_center, phantom_center, contrast_threshold, mtf_norm=none) Bases: pylinac.core.roi.diskroi A class for analyzing the high-contrast disks. contrast_threshold (float, int) The threshold for considering a bubble to be seen. mtf The contrast of the bubble compared to background: (ROI - backg) / (ROI + backg). passed Boolean specifying if ROI pixel value was within tolerance of the nominal value. plot_color Return one of two colors depending on if ROI passed. max The max pixel value of the ROI. min The min pixel value of the ROI. class pylinac.core.roi.rectangleroi(array, width, height, angle, dist_from_center, phantom_center) Bases: pylinac.core.geometry.rectangle Class that represents a rectangular ROI. pixel_array The pixel array within the ROI. 13.6 Mask Module Module for processing masked arrays, i.e. binary images. pylinac.core.mask.bounding_box(array) Get the bounding box values of an ROI in a 2D array. pylinac.core.mask.filled_area_ratio(array) Return the ratio of filled pixels to empty pixels in the ROI bounding box. For example a solid square would be 1.0, while a sold circle would be ~0.785. pylinac.core.mask.square_ratio(array) Determine the width/height ratio of the ROI pylinac.core.mask.sector_mask(shape, center, radius, angle_range=(0, 360)) Return a circular arc-shaped boolean mask. shape (tuple) Shape of the image matrix. Usually easiest to pass something like array.shape 13.6. Mask Module 133

center (Point, iterable) The center point of the desired mask. radius (int, float) Radius of the mask. angle_range (iterable) The angle range of the mask. E.g. the default (0, 360) creates an entire circle. The start/stop angles should be given in clockwise order. 0 is right (0 on unit circle). References https://stackoverflow.com/questions/18352973/mask-a-circular-sector-in-a-numpy-array/18354475#18354475 13.7 Utilities Module Utility functions for pylinac. pylinac.core.utilities.is_close(val, target, delta=1) Return whether the value is near the target value. pylinac.core.utilities.import_mpld3() Try importing MPLD3. Raises error if not installed. Returns the MPLD3 library. pylinac.core.utilities.typed_property(name, expected_type_or_tuple_of_types) Type-enforced property. Python Cookbook 9.21 (3rd ed). pylinac.core.utilities.simple_round(number, decimals=0) Round a number to the given number of decimals. Fixes small floating number errors. pylinac.core.utilities.is_dicom(file) Boolean specifying if file is a proper DICOM file. This function is a pared down version of read_preamble meant for a fast return. The file is read for a proper preamble ( DICM ), returning True if so, and False otherwise. This is a conservative approach. See also: file (str) The path to the file. pydicom.filereader.read_preamble(), pydicom.filereader.read_partial() pylinac.core.utilities.isnumeric(object) Check whether the passed object is numeric in any sense. pylinac.core.utilities.is_iterable(object) Determine if an object is iterable. pylinac.core.utilities.go_up_dirlevel(levels=0) Go up directory levels from where the caller file is located. levels (int) Specifies how many levels to go up. 0 goes to the current directory. 13.8 Decorators Module pylinac.core.decorators.timethis(func) Report execution time of function. pylinac.core.decorators.type_accept(*type_args, **type_kwargs) Decorator to check function/method input types. Based on Python Cookbook 3rd ed. #9.7. 134 Chapter 13. Core Modules Documentation

pylinac.core.decorators.value_accept(*value_args, **value_kwargs) Decorator to check function/method input types. Based on Python Cookbook 3rd ed. #9.7. pylinac.core.decorators.convert_dictvals2tuple(args) Convert from dictionary to tuple of dictionary values. 13.8. Decorators Module 135

136 Chapter 13. Core Modules Documentation

CHAPTER 14 Hacking your own tools with Pylinac Pylinac s main purpose is to make doing routine quality assurance easier and as automatic as possible. The main modules can be utilized in only a few lines of code and does a complete analysis of your QA images. However, when researching new tools, comparing specific algorithms, or doing custom testing, the default tools aren t appropriate. Pylinac s modules are built on simpler tools that can be used directly or easily extended. In this tutorial, we ll introduce some of pylinac s potential as a toolkit for customizing or creating new tests. Note: This is also viewable as a Jupyter notebook. 14.1 The image Module In many applications of QA, the physicist has images acquired via the EPID, kv imager, or scanned films. The image module provides easy tools for loading, manipulating, and converting these images. For this tutorial we ll use a few demo images and showcase the various methods available. There are a few simple tips for using the image module: The Image class is a swiss-army knife for loading images There are 3 related image classes to handle the given image types: Dicom, File, and Array The docs for the image API are here. First, our imports for the whole tutorial: %matplotlib inline from urllib.request import urlretrieve import matplotlib.pyplot as plt import numpy as np from pylinac.core.image import Image, DicomImage, FileImage, ArrayImage # pylinac demo images' URL PF_URL = 'https://github.com/jrkerns/pylinac/blob/master/pylinac/demo_files/picket_fence/epid-pf-lr.d STAR_URL = 'https://github.com/jrkerns/pylinac/blob/master/pylinac/demo_files/starshot/starshot.tif?r # local downloaded images PF_FILE, _ = urlretrieve(pf_url) STAR_FILE, _ = urlretrieve(star_url) 137

# sample numpy array ARR = np.arange(36).reshape((6,6)) 14.1.1 Loading images Let s load the picket fence demo image: pfimg = Image.load_url(PF_URL) type(pfimg) pylinac.core.image.dicomimage We can load the local file just as easily: pfimg = Image.load(PF_FILE) The load() and load_url() methods do not return an Image instance, but rather infers the type of image and returns one of the related Image types listed above. type(pfimg) pylinac.core.image.dicomimage The load() method can take an string pointing to a file, a data stream, or a numpy array: arr = np.arange(36).reshape((6,6)) img2 = Image.load(arr) #.pixel_array is a numpy array of DICOM file type(img2) pylinac.core.image.arrayimage Additionally, multiple images can be loaded and superimposed. For example multiple collimator star shots. Just pass a list of the images: star1, _ = urlretrieve('https://github.com/jrkerns/pylinac/blob/master/tests/test_files/starshot/set/ star2, _ = urlretrieve('https://github.com/jrkerns/pylinac/blob/master/tests/test_files/starshot/set/ superimposed_img = Image.load_multiples([star1, star2]) While the load() method will always do smart image inference for you, if you already know the file type you can instantiate directly. Furthermore, 2 keyword arguments can be passed to FileImage and ArrayImage if they are known: the DPI (dots-per-inch) and SID (source-to-image distance). dcm_img = DicomImage(PF_FILE) arr_img = ArrayImage(ARR, dpi=30, sid=500) file_img = FileImage(STAR_FILE, dpi=50, sid=1000) file_img2 = Image.load(STAR_FILE, dpi=30, sid=500) type(file_img) == type(file_img2) True For memory s sake we need to clean up a little. del file_img2, arr_img, superimposed_img, pfimg 138 Chapter 14. Hacking your own tools with Pylinac

14.1.2 Plotting Images can be easily plotted using the plot() method: dcm_img.plot() <matplotlib.axes._subplots.axessubplot at 0xdca4290> The plot can also be passed to an existing matplotlib axes: fig, ax = plt.subplots() dcm_img.plot(ax=ax) 14.1. The image Module 139

<matplotlib.axes._subplots.axessubplot at 0xb8aaf0> 14.1.3 Attributes The image classes contain a number of useful attributes for analyzing and describing the data: dcm_img.dpi 64.79591836734693 dcm_img.dpmm 2.5510204081632653 dcm_img.sid 1000.0 dcm_img.shape (768, 1024) There are also attributes that are useful in radiation therapy: dcm_img.cax # the beam CAX Point(x=512.00, y=384.00, z=0.00) dcm_img.center # the center location of the image Point(x=512.00, y=384.00, z=0.00) Above, the values are the same because the EPID was not translated, which would move the CAX but not the image center. 140 Chapter 14. Hacking your own tools with Pylinac

The image values can also be sampled by slicing and indexing: dcm_img[12, 60] 2124 dcm_img[:100, 82] array([1898, 1997, 1912, 2123, 1976, 2044, 2014, 1975, 1946, 1930, 1939, 2006, 2091, 1979, 2136, 2028, 1996, 2044, 2047, 2195, 2026, 2198, 2018, 2241, 2050, 2127, 2152, 2076, 2037, 2173, 2058, 2055, 2058, 2079, 2201, 2278, 2227, 2273, 2155, 2173, 2225, 2123, 2253, 2148, 2196, 2168, 2129, 2267, 2226, 2306, 2313, 2198, 2254, 2228, 2277, 2312, 2270, 2213, 2303, 2372, 2365, 2307, 2320, 2317, 2405, 2341, 2435, 2390, 2334, 2411, 2339, 2332, 2405, 2467, 2362, 2445, 2423, 2371, 2414, 2370, 2449, 2373, 2493, 2477, 2548, 2525, 2468, 2462, 2552, 2516, 2603, 2550, 2529, 2557, 2468, 2586, 2535, 2582, 2559, 2635], dtype=uint16) del dcm_img 14.1.4 Data manipulation Now the really fun stuff! There are many methods available to manipulate the data. First, let s smooth the data: file_img.median_filter(size=3) file_img.plot() <matplotlib.axes._subplots.axessubplot at 0x1215330> Sometimes starshots from scanned film have edges that are very high or low value (corners of the film can be bent or rounded). We can easily trim the edges: 14.1. The image Module 141

file_img.remove_edges(pixels=100) file_img.plot() <matplotlib.axes._subplots.axessubplot at 0xe460ff0> The data can also be explicitly inverted (EPID images oftentimes need this), or rolled on an axis: file_img.invert() file_img.plot() 142 Chapter 14. Hacking your own tools with Pylinac

<matplotlib.axes._subplots.axessubplot at 0xd4bdf0> file_img.roll(direction='y', amount=200) file_img.plot() <matplotlib.axes._subplots.axessubplot at 0xd6aed0> We can also rotate and resize the image: file_img.rot90(n=1) file_img.plot() 14.1. The image Module 143

<matplotlib.axes._subplots.axessubplot at 0xde1a50> file_img.resize(size=(1000, 1100)) file_img.plot() <matplotlib.axes._subplots.axessubplot at 0xdf5090> Scanned film values can be very high, even in low dose areas. We can thus ground the image so that the lowest value is zero; this will help us later on when detecting profiles. 144 Chapter 14. Hacking your own tools with Pylinac

np.min(file_img) 77.999969 file_img.ground() np.min(file_img) 0.0 file_img.plot() <matplotlib.axes._subplots.axessubplot at 0xe54670> We can also apply a high-pass filter to the image: thresh_val = np.percentile(file_img, 95) file_img.threshold(thresh_val) file_img.plot() 14.1. The image Module 145

<matplotlib.axes._subplots.axessubplot at 0xe89130> The image can also be converted to binary, which can be used later for ROI detection. Note that unlike any other method, this returns a new ArrayImage (hinted by the as_)... new_img = file_img.as_binary(thresh_val) new_img.plot() <matplotlib.axes._subplots.axessubplot at 0xe9c070>...and leaves the original unchanged. 146 Chapter 14. Hacking your own tools with Pylinac

file_img.plot() <matplotlib.axes._subplots.axessubplot at 0xe4dcef0> 14.2 The profile Module Physicists often need to evalute a profile, perhaps from a linac beam EPID image, or some fluence profile. The profile module allows the physicist to find peaks in a 1D array and determine beam profile information (FWHM, penumbra, etc). There are two main profile classes: SingleProfile - This class is for profiles with a single peak; e.g. an open beam delivered to a film or EPID. The main goal of this class is to describe the profile (FWHM, penumbra, etc). MultiProfile - This class is for profiles with multiple peaks. The main goal of this class is to find the peak or valley locations. A MultiProfile can be broken down into SingleProfiles. CircleProfile - A MultiProfile, but in the shape of a circle. CollapsedCircleProfile - A CircleProfile that collapses a thick ring of pixel data to create a 1D profile. The profile API docs are here. For this demonstration we ll find some peaks and then determine profile information about one of those peaks. Let s use the starshot demo image since it contains all the types of profiles: from pylinac.core.profile import SingleProfile, MultiProfile, CircleProfile, CollapsedCircleProfile STAR_URL = 'https://github.com/jrkerns/pylinac/blob/master/pylinac/demo_files/starshot/starshot.tif?r star_img = Image.load_url(STAR_URL) 14.2. The profile Module 147

14.2.1 Using a MultiProfile Let s start by sampling one row from the starshot image: row = star_img[800, :] plt.plot(row) [<matplotlib.lines.line2d at 0xe53acd0>] So, judging by the profile, it needs to be filtered for the spurious signals, it has multiple peaks, and it s upside down. Let s make a MultiProfile and clean up the data. mprof = MultiProfile(row) mprof.plot() 148 Chapter 14. Hacking your own tools with Pylinac

First, let s invert it so that pixel value increases with dose. mprof.invert() mprof.plot() We ve loaded the profile and inverted it; let s run a filter over it. mprof.filter(size=6) mprof.plot() 14.2. The profile Module 149

The profile could probably be filtered more since there s still a few spurious signals, but this will work nicely for our demonstration. First, we want to find the peak locations: mprof.find_peaks() array([ 642., 1272., 1914.]) The method has found the 3 major peaks of the profile. Note that there are actually 5 peaks if we count the spurious signals near indices 800 and 1200. For fun, let s see if we can detect these peaks. We can change the parameters to find_peaks() to optimize our search. mprof.find_peaks(threshold=0.1) array([ 642., 796., 1272., 1914.]) By lowering the peak height threshold we ve found another peak; but the peak near 1200 wasn t found. What gives? The find_peaks() method also eliminates peaks that are too close to one another. We can change that: mprof.find_peaks(threshold=0.1, min_distance=0.02) array([ 546., 642., 796., 1199., 1272., 1914.]) By changing the minimum distance peaks must be from each other, we ve found the other peak. But, let s say we need to use these settings for whatever reason. We can additionally limit the number of peaks using max_number. mprof.find_peaks(threshold=0.1, min_distance=0.02, max_number=3) array([ 1272., 1914., 642.]) 150 Chapter 14. Hacking your own tools with Pylinac

Now, we can visualize where these peaks are by using the plot() method, which shows the peaks if we ve searched for them; note the green dots at the detected peak locations. mprof.plot() We can also search a given portion of the region; for example if we only wanted to detect peaks in the first half of the profile we can easily add a search_region. Note that the last peak was not detected. mprof.find_peaks(search_region=(0, 0.6)) # search the left 60% of the profile array([ 642., 1272.]) We can search not simply for the max value peaks, but for the FWHM peaks. Note that these values are slightly different than the max value peaks we found earlier. mprof.find_fwxm_peaks(x=50) # 50 is 50% height [644.0, 1273.0, 1916.5] Finally, we can subdivide the profile into SingleProfile s to further describe single peaks: single_profiles = mprof.subdivide() # returns a list of SingleProfile's 14.2.2 Using a SingleProfile SingleProfiles are useful to describe profiles with a single peak. It can describe the FWXM (X=any height), penumbra on each side, field width, and calculations of the field. Continuing from above: sprof = single_profiles[0] sprof.plot() 14.2. The profile Module 151

The multiprofile has been cut into multiple single profiles, of which this is the first. Let s first find the FWHM, and the center of the FWHM: sprof.fwxm(x=50) 88 sprof.fwxm_center(x=50) 644.0 Note that this is the same value as the first FWHM peak value we found in the MultiProfile. We can now find the penumbra values: sprof.penumbra_width(side='left', upper=80, lower=20) 45 sprof.penumbra_width(upper=90, lower=10) # default is average of both sides 84 The careful reader will notice that the profiles, since we created them, has not had a minimum value of 0. Normally, this would cause problems and sometimes it does, but pylinac normalizes the FWXM and penumbra search. However, just to make sure all is well we can easily shift the values so that the lower bound is 0: sprof.ground() sprof.plot() 152 Chapter 14. Hacking your own tools with Pylinac

sprof.penumbra_width(upper=90, lower=10) 84 The average penumbra is the same as we found earlier. We can also normalize and stretch the profile values. Let s first get the original maximum value so we know what we need to restore the profile: np.max(sprof) 47.0 sprof.normalize() sprof.plot() 14.2. The profile Module 153

We can also stretch the values to be bounded by any values we please: sprof.stretch(min=30, max=284) sprof.plot() Let s restore our profile based on the earlier max and min values: sprof.stretch(max=47, min=0) sprof.plot() 154 Chapter 14. Hacking your own tools with Pylinac

14.2.3 Using CircleProfile and CollapsedCircleProfile Circular profiles are useful for concentric profiles; starshots are great examples. Let s explore the two circular profiles as they relate to a starshot image. Let s once again load the starshot demo image: star_img.plot() 14.2. The profile Module 155

<matplotlib.axes._subplots.axessubplot at 0xf656250> We saw above from the profile that the image is actually inverted (pixel value decreases with dose), so we need to invert the image: star_img.invert() To use a CircleProfile we need to specify the center and radius of the circle, as well as the array to operate over. The approximate center of the starshot is (1450, 1250). Let s also use a radius of 300 pixels. cprof = CircleProfile(center=(1250, 1450), radius=300, image_array=star_img) # center is given as ( cprof.plot() We have a nice profile showing the starshot peaks. It appears we ve cut one of the peaks in half though; this is because the profile starts at 0 radians on the unit circle (directly to the right) and there is a starshot line right at 0. We can also change the direction of the profile from the default of counter-clockwise to clockwise: cprof = CircleProfile(center=(1250, 1450), radius=300, image_array=star_img, start_angle=0.2, ccw=fal cprof.plot() 156 Chapter 14. Hacking your own tools with Pylinac

Alternatively, we can roll the profile directly: cprof.roll(amount=50) cprof.plot() Now, because CircleProfile is a subclass of MultiProfile we can search for the peaks: cprof.find_peaks() cprof.plot() 14.2. The profile Module 157

The profile is 1D, but was derived from a circular sampling. How do we know what the locations of the sampling is? We have x and y attributes: cprof.x_locations array([ 1530.05826457, 1529.69820368, 1529.33503504,..., 1531.11976086, 1530.7690471, 1530.4152137 ]) cprof.y_locations array([ 1557.55170128, 1558.48462959, 1559.41635252,..., 1554.74578777, 1555.68226998, 1556.61757795]) We can also add this profile to a plot to show where it is: ax = star_img.plot(show=false) cprof.plot2axes(ax) 158 Chapter 14. Hacking your own tools with Pylinac

Looks good! Now let s take a CollapsedCircleProfile. The advantage of this class is that a thick ring is sampled, which averages the pixel values. Thus, noise and spurious signals are reduced. Beyond CircleProfile there are 2 more keyword arguments: ccprof = CollapsedCircleProfile(center=(1250, 1450), radius=300, image_array=star_img, width_ratio=0. ccprof.plot() Note that this profile looks smoothed; this comes from averaging over the num_profiles within the ring. The width_ratio is a function of the radius, so in this case the actual ring width is 300*0.2 = 60 pixels, and 10 equally distributed profiles are taken within that ring. 14.2. The profile Module 159

Let s find the peaks and then plot the ring to the starshot image: ccprof.find_peaks() ccprof.plot() ax = star_img.plot(show=false) ccprof.plot2axes(ax) We now have a good start on a starshot algorithm! 160 Chapter 14. Hacking your own tools with Pylinac

CHAPTER 15 Troubleshooting 15.1 General Things always go wrong in real life. If you tried analyzing your data in pylinac and it threw an error, you can try a few simple things to fix it. First, See if the demo works - If not, pylinac may not have installed correctly or you may not have a dependency or the minimum version of a dependency. Second, Check the error - If it s an error that makes sense, maybe you just forgot something; e.g. analyzing an image before it s loaded will raise an error. Asking for a gamma result of a fluence before calculating the fluence will raise an error. Such things are easy to fix. Third, Check the Troubleshooting section of the specific module - Each module may fail in different ways, and also have different methods of resolution. And if none of those work, Post a question on the forum, email me, or file an issue on Github - You may have found a bug and it needs fixing! Forum Email Issue tracker 15.2 Loading TIFF Files Loading TIFF files can be tricky since there are many variations of the TIFF image format. Pillow is the package for image I/O in Python and is what pylinac uses. But sometimes even Pillow has trouble. If you ve tried loading a TIFF file and it just doesn t seem to be working you can try two things: Install pillow with conda; when installing this way more libraries are installed (vs. pip )that pillow can leverage. Resave the image with another program. While I can t tell you exactly what will work, one solution that s worked for me is using GIMP. It s free; just open up your TIFF files and then export them back to TIFF. It may not seem like that should change anything, but my anecdotal evidence is that every TIFF image that didn t work that I reconverted using GIMP allowed me to read it in, especially when combined with the above strategy. 161

162 Chapter 15. Troubleshooting

CHAPTER 16 Changelog 16.1 V 1.3.1 (#46) - Fixes CBCT analysis where there is a ring artifact outside the phantom. Incidentally, analysis is sped up by ~10%. 16.2 V 1.3.0 16.2.1 General Changes A new dependency has been added: scikit-image. Given that pylinac is largely an image processing library, this is actually overdue. Several extremely helpful functions exist that are made use of in both the new modules and will slowly be incorporated into the old modules as needed. The package is easily installed via pip (pip install scikit-image) or via conda (conda install scikit-image) if using the Anaconda distribution. Finally, if simply upgrading pylinac scikit-image will automatically install via pip. For the sake of installation speed I d recommend conda. ROI sampling for CBCT and Leeds classes have been sped up ~10x, making analysis moderately to much faster. All user-interface dialog functions/methods have been deprecated. E.g. PicketFence.from_UI() is no longer a valid method. To retain similar functionality use Tk to open your own dialog box and then pass in the file name. Specifically, this applies to the VMAT, Starshot, PicketFence, MachineLog(s), FlatSym, and CBCT classes. The original goal of pylinac was to be used for a standalone desktop application. The assuranceqa.com web interface is the successor to that idea and does not need those UI methods. 16.2.2 Planar Imaging A new planar imaging class has been added: PipsProQC3. This class analyzes the PipsPro QC-3 MV imaging phantom. The class locates and analyzes low and high contrast ROIs. The Leeds phantom utilizes the scikit-image library to do a canny edge search to find the phantom. This will bring more stability for this class. 16.3 V 1.2.2 (#45) Fixes various crashes of Leeds analysis. 163

16.4 V 1.2.1 (#44) Fixed a stale wheel build causing pip install to install v1.1. 16.5 V 1.2.0 16.5.1 General Changes CatPhan 503 (Elekta) analysis is now supported. A new planar imaging module has been added for 2D phantom analysis; currently the Leeds TOR phantom is available. The requests package is no longer needed for downloading URLs; the urllib stdlib module is now used instead. Requirements were fixed in the docs and setup.py; a numpy function was being used that was introduced in v1.9 even though v1.8 was stated as the minimum; the new requirement is v1.9. Demonstration methods for the main classes have been fully converted to static methods. This means, for example, the following are equivalent: CBCT().run_demo() and CBCT.run_demo(). 16.5.2 Core Modules A tutorial on the use of the core modules is now available. A new mask core module was created for binary array operations. (#42) The Image classes now have a gamma method available. The Image classes median_filter() method has been renamed to filter(), which allows for different types of filters to be passed in. The Image class can now load directly from a URL: load_url(). 16.5.3 CBCT CatPhan 503 (Elekta) is now supported. Usage is exactly the same except for the low-contrast module, which is not present in the 503. The low contrast measurements now use two background bubbles on either side of each contrast ROI. The default contrast threshold has been bumped to 15, which is still arbitrary but fits most eyeball values. 16.5.4 Starshot (#43) Keyword arguments can be passed to the init and class methods regarding the image info. For example, if a.tif file is loaded but the DPI is not in the image header it can be passed in like so: star = Starshot('mystar.tif', dpi=100, sid=1000) 164 Chapter 16. Changelog

16.5.5 Planar Imaging 2D analysis of the Leeds TOR phantom is available. Tests low and high contrast. A new Planar Imaging module documentation doc page has been created. 16.5.6 Winston-Lutz A save_summary() method has been added for saving the plot to file. 16.6 V 1.1.1 Winston-Lutz demo images were not included in the pypi package. 16.7 V 1.1.0 16.7.1 General Changes This release debuts the new Winston-Lutz module, which easily loads any number of EPID images, finds the field CAX and the BB, and can plot various metrics. 16.7.2 Log Analyzer Logs can now be anonymized using the.anonymize() method for both MachineLog and MachineLogs. The.to_csv() methods for MachineLog and MachineLogs returns a list of the newly created files. MachineLogs can now load from a zip archive using.from_zip(). 16.8 V 1.0.3 Fixes #39. MachineLog fluence was inverted in the left-right direction. Fixes #40. MachineLog fluence calculations from dynalogs were dependent on the load order (A-file vs. B-file). 16.9 V 1.0.2 Fixes #38. MachineLog fluence calculations would crash if there was no beam-on snapshots (e.g. kv images). 16.10 V 1.0.1 Fixes #37. Reading in a trajectory log txt file with a blank line caused a crash. 16.6. V 1.1.1 165

16.11 V 1.0.0 16.11.1 General Changes This release debuts the new interactive plotting for certain figures. Quickly, matplotlib line/bar plots (althouth not yet images/arrays) can be plotted and saved in HTML using the MPLD3 library. This is less of interest to users doing interactive work, but this adds the ability to embed HTML plots in web pages. Several numpy array indexing calls were converted to ints from floats to avoid the new 1.9 numpy type-casting warnings. This also speeds up indexing calls slightly. 16.11.2 Picket Fence The analyzed image now has the option of showing a leaf error subplot beside the image. The image is aligned to the image such that the leaves align with the image. 16.11.3 Starshot Plotting the analyzed starshot image now shows both the zoomed-out image and a second, zoomed-in view of the wobble. Each subplot can be plotted and saved individually. 16.11.4 VMAT Plotting the analyzed image now shows the open and dmlc images and the segment outlines as well as a profile comparison between the two images. Each subplot can also be plotted and saved individually. MLCS is no longer a test option; DRMLC should be used instead. 16.12 V 0.9.1 Fixed a bug with the log analyzer treatment type property. 16.13 V 0.9.0 16.13.1 General Changes This release has a few new features for the CBCT class, but is mostly an internal improvement. If you only use the main classes (CBCT, PicketFence, Starshot, etc), there should be no changes needed. 16.13.2 CBCT The CBCT analysis now examines low contrast ROIs and slice thickness. CBCT components have been renamed. E.g. the HU linearity attr has been renamed hu from HU. 166 Chapter 16. Changelog

16.13.3 Starshot Fixes #32 which was causing FWHM peaks on starshots to sometimes be erroneous for uint8/uint16 images. 16.13.4 PicketFence Adds #31, a method for loading multiple images into PicketFence. 16.13.5 Log Analyzer Fixes a bug which sometimes caused the parsing of the associated.txt log file for trajectory logs to crash. 16.14 V 0.8.2 Fixed a bug with the picket fence overlay for left-right picket patterns. Plots for starshot, vmat, and picketfence now have a larger DPI, which should mean some more detail for saved images. 16.15 V 0.8.1 Fixed an import bug 16.16 V 0.8.0 16.16.1 General Changes An upgrade for the robustness of the package. A LOT of test images were added for the Starshot, CBCT, PicketFence, and VMAT modules and numerous bugs were caught and fixed in the process. The debut of the directory watcher. Run this script to tell pylinac to watch a directory; if a file with certain keywords is placed in the directory, pylinac will analyze the image and output the analyzed image and text file of results in the same directory. A generic troubleshooting section has been added to the documentation, and several modules have specific troubleshooting sections to help identify common errors and how to fix them. 16.16.2 VMAT Added a from_zip() and load_zip() method to load a set of images that are in a zip file. Added an x_offset parameter to analyze() to make shifting segments easier. 16.14. V 0.8.2 167

16.16.3 PicketFence Fixed #30, which wasn t catching errors on one side of the pickets, due to a signed error that should ve been absolute. Two new parameters have been added to analyze(): num_pickets and sag_adjustment, which are somewhat self-explanatory. Consult the docs for more info. 16.16.4 Starshot Fixed #29, which was causing analysis to fail for images with a pin prick. 16.16.5 CBCT Fixed #28, which was applying the phantom roll adjustment the wrong direction. 16.17 V 0.7.1 16.17.1 General Changes Added.from_url() class method and.load_url() methods to most modules. 16.17.2 PicketFence Fixed #23, which was not properly detecting pickets for picket patterns that covered less than half the image. Fixed #24, which was failing analysis from small but very large noise. A small median filter is now applied to images upon loading. 16.18 V 0.7.0 16.18.1 General Changes The scipy dependency has been bumped to v0.15 to accommodate the new differential evolution function using in the Starshot module. 16.18.2 CBCT Whereas v0.6 attempted to fix an issue where if the phantom was not centered in the scan it would error out by adding a z-offset, v0.7 is a move away from this idea. If the offset given was not correct then analysis would error disgracefully. It is the point of automation to automatically detect things like where the phantom is in the dataset. Thus, v0.7 is a move towards this goal. Briefly, upon loading all the images are scanned and the HU linearity slice is searched for. Of the detected slices, the median value is taken. Other slices are known relative to this position. As per above, the z-offset idea is no longer used or allowed. Plots are now all shown in grayscale. 168 Chapter 16. Changelog

If the phantom was not completely scanned (at least the 4 modules of analysis) analysis will now error out more gracefully. 16.19 V 0.6.0 16.19.1 General Changes Pylinac now has a wheel variation. Installation should thus be quicker for users with Python 3.4. Most main module classes now have a save method to save the image that is plotted by the plot method. Class-based Constructors This release presents a normalized and new way of loading and initializing classes for the PicketFence, Starshot, VMAT and CBCT classes. Those classes all now accept the image path (folder path for CBCT) in the initialization method. Loading other types of data should be delegated to class-based constructors (e.g. to load a zip file into the CBCT class, one would use cbct = CBCT.from_zip_file( zfiles.zip )). This allows the user to both initialize and load the images/data in one step. Also prevents user from using methods before initialization (i.e. safer). See ReadTheDocs page for more info. Dependencies Because the VMAT module was reworked and is now based on Varian specs, the pandas package will no longer be required. FutureWarnings have been removed. 16.19.2 CBCT Bug #18 is fixed. This bug did not account for slice thickness when determining the slice positions of the relevant slices. Bug #19 is fixed. This bug allowed the loading of images that did not belong to the same study. An error is now raised if such behavior is observed. Demo files are now read from the zipfile, rather than being extracted and then potentially cleaning up afterward. Behavior is now quicker and cleaner. Individual plots of certain module/slices can now be done. Additionally, the MTF can be plotted. The user can now adjust the relative position of the slice locations in the event the phantom is not set up to calibration conditions. 16.19.3 Log Analyzer Keys in the txt attr dict weren t stripped and could have trailing spaces. Keys are now stripped. 16.19.4 VMAT Ability to offset the segments has been added. Complete overhaul to conform to new Varian RapidArc QA specs. This includes the following: Rather than individual samples, 4 or 7 segments are created, 5x100mm each. 16.19. V 0.6.0 169

Deviation is now calculated for each segment, based on the average segment value. The DRMLC test has changed name to MLCS. E.g. passing a test should be: myvmat.analyze( mlcs ), not myvmat.analyze( drmlc ); the latter will still work but raises a future warning. 16.19.5 Starshot Fixed a bug where an image that did not have pixels/mm information would error out. Added a tolerance parameter to the analyze method. 16.20 V 0.5.1 16.20.1 Log Analyzer Axis limits are now tightened to the data when plotting log_analyzer.axis data. Gamma map plot luminescence is now normalized to 1 and a colorbar was added. Bug #14 fixed, where Tlogs v3 were not loading couch information properly. Trajectory log.txt files now also load along with the.bin file if one is around. 16.20.2 Starshot Multiple images can now be superimposed to form one image for analysis. 16.20.3 VMAT load_demo_image() parameter changed from test_type to type 16.21 V 0.5.0 A new flatness & symmetry module allows for film and EPID image analysis. The log_analyzer module now supports writing trajectory logs to CSV. A FutureWarning that pandas will be a dependency in later versions if it s not installed. 16.22 V 0.4.1 Batch processing of logs added via a new class. ~4x speedup of fluence calculations. 16.23 V 0.4.0 A Varian MLC picket fence analysis module was added; this will analyze EPID PF images of any size and either orientation. 170 Chapter 16. Changelog

16.24 V 0.3.0 Log Analyzer module added; this module reads Dynalogs and Trajectory logs from Varian linear accelerators. 16.24.1 Starshot The profile circle now aligns with the lines found. Recursive option added to analyze for recursive searching of a reasonable wobble. Image now has a cleaner interface and properties 16.25 V 0.2.1 Demo files were not included when installed from pip 16.26 V 0.2.0 Python 2.7 support dropped. Python 3 has a number of features that Python 2 does not, and because this project is just getting started, I didn t want to support Python 2, and then eventually drop it as Python 3 becomes more and more mainstream. Internal overhaul. Modules are now in the root folder. A core module with specialized submodules was created with a number of various tools. Demo files were assimilated into one directory with respective subdirectories. VMAT module can now handle HDMLC images. CBCT module was restructured and is much more reliable now. method names normalized, specifically the return_results method, which had different names in different modules. Lots of tests added; coverage increased dramatically. 16.27 V 0.1.3 Overall A module for analyzing CBCT DICOM acquisitions of a CatPhan 504 (Varian) has been added. The starshot demo files have been compressed to zip files to save space. A value decorator was added for certain functions to enforce, e.g., ranges of values that are acceptable. The Files directory was moved outside the source directory. -Starshot now reports the diameter instead of radius 16.28 V 0.1.2 A PyPI setup.py bug was not properly installing pylinac nor including demo files. Both of these have been fixed. 16.24. V 0.3.0 171

16.29 V 0.1.1 Several small bugs were fixed and small optimizations made. A few methods were refactored for similarity between modules. 16.30 V 0.1.0 This is the initial release of Pylinac. It includes two modules for doing TG-142-related tasks: Starshot & VMAT QA Versioning mostly follows standard semantic revisioning. However, each new module will result in a bump in minor release, while bug fixes will bump patch number. 172 Chapter 16. Changelog

CHAPTER 17 Indices and tables genindex modindex search 173

174 Chapter 17. Indices and tables

Python Module Index p pylinac.cbct, 31 pylinac.core.decorators, 134 pylinac.core.geometry, 122 pylinac.core.image, 117 pylinac.core.io, 131 pylinac.core.mask, 133 pylinac.core.profile, 124 pylinac.core.roi, 132 pylinac.core.utilities, 134 pylinac.log_analyzer, 47 pylinac.picketfence, 75 pylinac.planar_imaging, 95 pylinac.starshot, 11 pylinac.vmat, 21 pylinac.winston_lutz, 87 175

176 Python Module Index

Index A abs_median_error (pylinac.picketfence.picket attribute), 85 abs_median_error (pylinac.picketfence.picketfence attribute), 82 actual (pylinac.log_analyzer.fluencestruct attribute), 71 ActualFluence (class in pylinac.log_analyzer), 72 add_guards_to_axes() (pylinac.picketfence.picket method), 86 add_leaf_axis() (pylinac.log_analyzer.mlc method), 64 add_mlc_meas() (pylinac.picketfence.picket method), 85 add_to_axes() (pylinac.picketfence.overlay method), 86 air_bubble_radius_mm (pylinac.cbct.settings attribute), 38 air_bubble_size (pylinac.cbct.settings attribute), 38 analyze() (pylinac.cbct.cbct method), 38 analyze() (pylinac.picketfence.picketfence method), 83 analyze() (pylinac.planar_imaging.leedstor method), 104 analyze() (pylinac.planar_imaging.pipsproqc3 method), 106 analyze() (pylinac.starshot.starshot method), 16 analyze() (pylinac.vmat.vmat method), 27 AnalyzeCBCT (class in pylinac.watcher), 114 AnalyzeLog (class in pylinac.watcher), 115 AnalyzeMixin (class in pylinac.watcher), 114 AnalyzePF (class in pylinac.watcher), 114 AnalyzeStar (class in pylinac.watcher), 114 AnalyzeVMAT (class in pylinac.watcher), 115 anonymize() (pylinac.log_analyzer.machinelog method), 61 anonymize() (pylinac.log_analyzer.machinelogs method), 59 append() (pylinac.log_analyzer.machinelogs method), 58 args (AnalyzeMixin attribute), 114 array (pylinac.core.image.imagemixin attribute), 118 ArrayImage (class in pylinac.core.image), 121 as_array() (pylinac.core.geometry.point method), 123 as_binary() (pylinac.core.image.imagemixin method), 119 as_scalar() (pylinac.core.geometry.vector method), 122 avg_abs_r_deviation (pylinac.vmat.segmentmanager attribute), 29 avg_abs_r_deviation (pylinac.vmat.vmat attribute), 27 avg_gamma (pylinac.log_analyzer.gammafluence attribute), 73 avg_gamma() (pylinac.log_analyzer.machinelogs method), 58 avg_gamma_pct() (pylinac.log_analyzer.machinelogs method), 58 avg_r_deviation (pylinac.vmat.segmentmanager attribute), 29 avg_r_deviation (pylinac.vmat.vmat attribute), 27 avg_slice_thickness (pylinac.cbct.thicknessslice attribute), 43 Axis (class in pylinac.log_analyzer), 62 axis_data (pylinac.log_analyzer.machinelog attribute), 60 axis_enum (pylinac.log_analyzer.tlogheader attribute), 68 axis_scale (pylinac.log_analyzer.tlogheader attribute), 69 B b (pylinac.core.geometry.line attribute), 123 bbox_center() (in module pylinac.core.roi), 132 beam_hold (pylinac.log_analyzer.dlogaxisdata attribute), 67 beam_hold (pylinac.log_analyzer.tlogaxisdata attribute), 69 beam_name (pylinac.log_analyzer.subbeam attribute), 70 beam_on (pylinac.log_analyzer.dlogaxisdata attribute), 67 BeamImage (class in pylinac.flatsym), 110 bg_color (pylinac.picketfence.mlcmeas attribute), 86 bg_roi_radius (pylinac.cbct.lowcontrastslice attribute), 43 bounding_box() (in module pylinac.core.mask), 133 177

C calc_map() (pylinac.log_analyzer.fluence method), 71 calc_map() (pylinac.log_analyzer.gammafluence method), 73 carriage_a (pylinac.log_analyzer.dlogaxisdata attribute), 68 carriage_a (pylinac.log_analyzer.tlogaxisdata attribute), 70 carriage_b (pylinac.log_analyzer.dlogaxisdata attribute), 68 carriage_b (pylinac.log_analyzer.tlogaxisdata attribute), 70 cax (pylinac.core.image.dicomimage attribute), 120 cax2bb_distance (pylinac.winston_lutz.wlimage attribute), 92 cax2bb_distance() (pylinac.winston_lutz.winstonlutz method), 91 cax2bb_vector (pylinac.winston_lutz.wlimage attribute), 92 cax_line_projection (pylinac.winston_lutz.wlimage attribute), 92 CBCT (class in pylinac.cbct), 35 ccw (pylinac.core.profile.circleprofile attribute), 129 center (pylinac.core.geometry.line attribute), 124 center (pylinac.core.image.imagemixin attribute), 118 check_inversion() (pylinac.core.image.imagemixin method), 119 Circle (class in pylinac.core.geometry), 123 circle_mask() (pylinac.cbct.diskroi method), 44 circle_mask() (pylinac.core.roi.diskroi method), 132 circle_profile (pylinac.cbct.spatialresolutionslice attribute), 42 circle_profile (pylinac.starshot.starshot attribute), 14 CircleProfile (class in pylinac.core.profile), 129 clinac_scale (pylinac.log_analyzer.dlogheader attribute), 67 CollapsedCircleProfile (class in pylinac.core.profile), 130 collimator (pylinac.log_analyzer.dlogaxisdata attribute), 67 collimator (pylinac.log_analyzer.tlogaxisdata attribute), 69 collimator_angle (pylinac.log_analyzer.subbeam attribute), 70 collimator_angle (pylinac.winston_lutz.wlimage attribute), 92 collimator_iso2bb_vector (pylinac.winston_lutz.winstonlutz attribute), 91 collimator_iso_size (pylinac.winston_lutz.winstonlutz attribute), 91 construct_rad_lines() (pylinac.starshot.linemanager method), 18 contrast (pylinac.cbct.lowcontrastdiskroi attribute), 45 contrast (pylinac.core.roi.lowcontrastdiskroi attribute), 132 contrast_constant (pylinac.cbct.lowcontrastdiskroi attribute), 45 contrast_constant (pylinac.core.roi.lowcontrastdiskroi attribute), 132 contrast_to_noise (pylinac.cbct.lowcontrastdiskroi attribute), 45 contrast_to_noise (pylinac.core.roi.lowcontrastdiskroi attribute), 132 control_point (pylinac.log_analyzer.subbeam attribute), 70 control_point (pylinac.log_analyzer.tlogaxisdata attribute), 70 convert_dictvals2tuple() (in module pylinac.core.decorators), 135 cos() (in module pylinac.core.geometry), 122 couch (pylinac.log_analyzer.tlogaxisdata attribute), 69 couch_angle (pylinac.winston_lutz.wlimage attribute), 92 couch_iso2bb_vector (pylinac.winston_lutz.winstonlutz attribute), 91 couch_iso_size (pylinac.winston_lutz.winstonlutz attribute), 91 CouchStruct (class in pylinac.log_analyzer), 74 create_error_array() method), 65 create_rms_array() method), 66 (pylinac.log_analyzer.mlc (pylinac.log_analyzer.mlc D diameter (pylinac.core.geometry.circle attribute), 123 dicom_stack (pylinac.cbct.cbct attribute), 35 DicomImage (class in pylinac.core.image), 120 DicomImageStack (class in pylinac.core.image), 121 difference (pylinac.log_analyzer.axis attribute), 63 DiskROI (class in pylinac.cbct), 44 DiskROI (class in pylinac.core.roi), 132 dist2edge_min() (pylinac.core.image.imagemixin method), 119 dist2rois (pylinac.cbct.roimanagermixin attribute), 44 dist2rois_mm (pylinac.cbct.roimanagermixin attribute), 44 distance_to() (pylinac.core.geometry.line method), 124 distance_to() (pylinac.core.geometry.point method), 123 distance_to() (pylinac.core.geometry.vector method), 122 distta (pylinac.log_analyzer.gammafluence attribute), 72 DlogAxisData (class in pylinac.log_analyzer), 67 DlogHeader (class in pylinac.log_analyzer), 66 dmlc_img_is_loaded (pylinac.vmat.vmat attribute), 27 doseta (pylinac.log_analyzer.gammafluence attribute), 72 dpi (pylinac.core.image.arrayimage attribute), 121 178 Index

dpi (pylinac.core.image.dicomimage attribute), 120 dpi (pylinac.core.image.fileimage attribute), 121 dpmm (pylinac.core.image.arrayimage attribute), 121 dpmm (pylinac.core.image.dicomimage attribute), 120 dpmm (pylinac.core.image.fileimage attribute), 121 draw() (pylinac.vmat.segmentmanager method), 29 E error (pylinac.picketfence.mlcmeas attribute), 86 error_array (pylinac.picketfence.picket attribute), 85 error_hist() (pylinac.picketfence.pickethandler method), 84 expected (pylinac.log_analyzer.fluencestruct attribute), 71 expected_phantom_size (pylinac.cbct.settings attribute), 39 ExpectedFluence (class in pylinac.log_analyzer), 72 F field_calculation() (pylinac.core.profile.singleprofile method), 127 field_edges() (pylinac.core.profile.singleprofile method), 126 field_values() (pylinac.core.profile.singleprofile method), 126 figure_size (pylinac.picketfence.settings attribute), 85 FileImage (class in pylinac.core.image), 120 filename (pylinac.log_analyzer.machinelog attribute), 60 filled_area_ratio() (in module pylinac.core.mask), 133 filter() (pylinac.core.image.imagemixin method), 118 filter() (pylinac.core.profile.profilemixin method), 125 find_fwxm_peaks() (pylinac.core.profile.circleprofile method), 129 find_fwxm_peaks() (pylinac.core.profile.multiprofile method), 128 find_mlc_peak() (pylinac.picketfence.picket method), 85 find_peaks() (pylinac.core.profile.circleprofile method), 129 find_peaks() (pylinac.core.profile.multiprofile method), 127 find_pickets() (pylinac.picketfence.pickethandler method), 84 find_valleys() (pylinac.core.profile.circleprofile method), 129 find_valleys() (pylinac.core.profile.multiprofile method), 128 fit (pylinac.picketfence.picket attribute), 85 flatness() (pylinac.flatsym.beamimage method), 111 Fluence (class in pylinac.log_analyzer), 71 fluence (pylinac.log_analyzer.machinelog attribute), 60 FluenceStruct (class in pylinac.log_analyzer), 71 from_demo_image() (pylinac.picketfence.picketfence class method), 83 from_demo_image() (pylinac.planar_imaging.leedstor method), 105 from_demo_image() (pylinac.planar_imaging.pipsproqc3 method), 106 from_demo_image() (pylinac.starshot.starshot class method), 15 from_demo_images() (pylinac.cbct.cbct class method), 36 from_demo_images() (pylinac.vmat.vmat class method), 26 from_demo_images() (pylinac.winston_lutz.winstonlutz class method), 90 from_multiple_images() (pylinac.picketfence.picketfence class method), 83 from_multiple_images() (pylinac.starshot.starshot class method), 16 from_url() (pylinac.cbct.cbct class method), 36 from_url() (pylinac.log_analyzer.machinelog class method), 61 from_url() (pylinac.picketfence.picketfence class method), 82 from_url() (pylinac.planar_imaging.leedstor method), 105 from_url() (pylinac.planar_imaging.pipsproqc3 method), 106 from_url() (pylinac.starshot.starshot class method), 15 from_urls() (pylinac.vmat.vmat class method), 27 from_zip() (pylinac.core.image.dicomimagestack class method), 122 from_zip() (pylinac.log_analyzer.machinelogs class method), 58 from_zip() (pylinac.vmat.vmat class method), 26 from_zip() (pylinac.winston_lutz.winstonlutz class method), 90 from_zip_file() (pylinac.cbct.cbct class method), 36 fwxm() (pylinac.core.profile.singleprofile method), 126 fwxm_center() (pylinac.core.profile.singleprofile method), 126 G gamma (pylinac.log_analyzer.fluencestruct attribute), 71 gamma() (pylinac.core.image.imagemixin method), 119 GammaFluence (class in pylinac.log_analyzer), 72 gantry (pylinac.log_analyzer.dlogaxisdata attribute), 67 gantry (pylinac.log_analyzer.tlogaxisdata attribute), 69 gantry_angle (pylinac.log_analyzer.subbeam attribute), 70 gantry_angle (pylinac.winston_lutz.wlimage attribute), 92 gantry_iso2bb_vector (pylinac.winston_lutz.winstonlutz attribute), 91 gantry_iso_size (pylinac.winston_lutz.winstonlutz attribute), 90 Index 179

gantry_sag() (pylinac.winston_lutz.winstonlutz method), 91 GeoDiskROI (class in pylinac.cbct), 45 GeometricLine (class in pylinac.cbct), 46 geometry (pylinac.cbct.cbct attribute), 35 GeometrySlice (class in pylinac.cbct), 40 get_bg_color() (pylinac.vmat.segment method), 29 get_error_percentile() (pylinac.log_analyzer.mlc method), 65 get_leaves() (pylinac.log_analyzer.mlc method), 65 get_peaks() (pylinac.starshot.starprofile method), 18 get_rms() (pylinac.log_analyzer.mlc method), 65 get_rms_avg() (pylinac.log_analyzer.mlc method), 64 get_rms_max() (pylinac.log_analyzer.mlc method), 64 get_rms_percentile() (pylinac.log_analyzer.mlc method), 64 get_roi_vals() (pylinac.cbct.roimanagermixin method), 44 get_snapshot_values() (pylinac.log_analyzer.mlc method), 66 get_url() (in module pylinac.core.io), 131 go_up_dirlevel() (in module pylinac.core.utilities), 134 ground() (pylinac.core.image.imagemixin method), 119 ground() (pylinac.core.profile.profilemixin method), 125 H hc_ref_rois (pylinac.planar_imaging.leedstor attribute), 104 hc_rois (pylinac.planar_imaging.leedstor attribute), 104 hc_rois (pylinac.planar_imaging.pipsproqc3 attribute), 105 header (pylinac.log_analyzer.machinelog attribute), 60 header (pylinac.log_analyzer.tlogheader attribute), 68 header_size (pylinac.log_analyzer.tlogheader attribute), 68 HighContrastDiskROI (class in pylinac.core.roi), 133 histogram() (pylinac.log_analyzer.gammafluence method), 73 hu (pylinac.cbct.cbct attribute), 35 hu_slice_num (pylinac.cbct.settings attribute), 39 HUDiskROI (class in pylinac.cbct), 45 HUSlice (class in pylinac.cbct), 40 I Image (class in pylinac.core.image), 117 image (pylinac.picketfence.picketfence attribute), 82 image (pylinac.starshot.starshot attribute), 14 image_array (pylinac.core.profile.circleprofile attribute), 129 image_dmlc (pylinac.vmat.vmat attribute), 25 image_is_loaded (pylinac.starshot.starshot attribute), 17 image_mlc_inplane_mean_profile (pylinac.picketfence.pickethandler attribute), 84 image_open (pylinac.vmat.vmat attribute), 25 ImageManager (class in pylinac.winston_lutz), 92 ImageMixin (class in pylinac.core.image), 118 images (pylinac.core.image.dicomimagestack attribute), 121 images (pylinac.winston_lutz.winstonlutz attribute), 90 images_loaded (pylinac.cbct.cbct attribute), 38 img_filename (pylinac.watcher.analyzemixin attribute), 114 import_mpld3() (in module pylinac.core.utilities), 134 info (pylinac.core.image.fileimage attribute), 120 invert() (pylinac.core.image.imagemixin method), 119 invert() (pylinac.core.profile.profilemixin method), 125 is_close() (in module pylinac.core.utilities), 134 is_dicom() (in module pylinac.core.utilities), 134 is_iterable() (in module pylinac.core.utilities), 134 is_loaded (pylinac.log_analyzer.machinelog attribute), 62 is_truncated (pylinac.log_analyzer.tlogheader attribute), 69 is_valid_dir() (in module pylinac.core.io), 131 is_valid_file() (in module pylinac.core.io), 131 isnumeric() (in module pylinac.core.utilities), 134 J jaw_x1 (pylinac.log_analyzer.subbeam attribute), 70 jaw_x2 (pylinac.log_analyzer.subbeam attribute), 70 jaw_y1 (pylinac.log_analyzer.subbeam attribute), 71 jaw_y2 (pylinac.log_analyzer.subbeam attribute), 71 jaws (pylinac.log_analyzer.dlogaxisdata attribute), 68 jaws (pylinac.log_analyzer.tlogaxisdata attribute), 69 JawStruct (class in pylinac.log_analyzer), 74 K keywords (AnalyzeMixin attribute), 114 L large_leaf_width (pylinac.picketfence.settings attribute), 85 latl (pylinac.log_analyzer.couchstruct attribute), 74 lc_ref_rois (pylinac.planar_imaging.leedstor attribute), 104 lc_ref_rois (pylinac.planar_imaging.pipsproqc3 attribute), 105 lc_rois (pylinac.planar_imaging.leedstor attribute), 104 lc_rois (pylinac.planar_imaging.pipsproqc3 attribute), 105 lc_slice_num (pylinac.cbct.settings attribute), 39 leaf_axes (pylinac.log_analyzer.mlc attribute), 63 leaf_centers (pylinac.picketfence.settings attribute), 85 180 Index

leaf_moved() (pylinac.log_analyzer.mlc method), 64 leaf_pair (pylinac.picketfence.mlcmeas attribute), 86 leaf_under_y_jaw() (pylinac.log_analyzer.mlc method), 66 LeedsTOR (class in pylinac.planar_imaging), 104 left_guard (pylinac.picketfence.picket attribute), 86 length (pylinac.core.geometry.line attribute), 124 length_mm (pylinac.cbct.geometricline attribute), 46 Line (class in pylinac.core.geometry), 123 line_assignments (pylinac.cbct.geometryslice attribute), 41 line_pair_cutoff (pylinac.cbct.spatialresolutionslice attribute), 41 line_pair_frequency (pylinac.cbct.spatialresolutionslice attribute), 41 line_pair_mtfs (pylinac.cbct.spatialresolutionslice attribute), 42 LineManager (class in pylinac.starshot), 18 lines (pylinac.cbct.geometryslice attribute), 41 lines (pylinac.starshot.starshot attribute), 14 load() (pylinac.core.image.image class method), 118 load() (pylinac.log_analyzer.machinelog method), 61 load_demo_dynalog() (pylinac.log_analyzer.machinelog method), 60 load_demo_image() (pylinac.flatsym.beamimage method), 110 load_demo_image() (pylinac.picketfence.picketfence method), 83 load_demo_image() (pylinac.starshot.starshot method), 15 load_demo_image() (pylinac.vmat.vmat method), 26 load_demo_images() (pylinac.cbct.cbct method), 36 load_demo_trajectorylog() (pylinac.log_analyzer.machinelog method), 61 load_folder() (pylinac.cbct.cbct method), 36 load_folder() (pylinac.log_analyzer.machinelogs method), 58 load_image() (pylinac.picketfence.picketfence method), 83 load_image() (pylinac.starshot.starshot method), 15 load_image() (pylinac.vmat.vmat method), 26 load_images() (pylinac.vmat.vmat method), 26 load_multiple_images() (pylinac.picketfence.picketfence method), 83 load_multiple_images() (pylinac.starshot.starshot method), 16 load_multiples() (pylinac.core.image.image class method), 118 load_url() (pylinac.cbct.cbct method), 36 load_url() (pylinac.core.image.image class method), 118 load_url() (pylinac.log_analyzer.machinelog method), 61 load_url() (pylinac.picketfence.picketfence method), 82 load_url() (pylinac.starshot.starshot method), 15 load_urls() (pylinac.vmat.vmat method), 27 load_zip() (pylinac.vmat.vmat method), 26 load_zip_file() (pylinac.cbct.cbct method), 36 load_zipfile() (in module pylinac.core.io), 131 log_is_loaded (pylinac.log_analyzer.machinelog attribute), 60 log_type (pylinac.log_analyzer.machinelog attribute), 60, 62 long (pylinac.log_analyzer.couchstruct attribute), 74 long_profile (pylinac.cbct.thicknessroi attribute), 46 lowcontrast (pylinac.cbct.cbct attribute), 35 LowContrastDiskROI (class in pylinac.cbct), 45 LowContrastDiskROI (class in pylinac.core.roi), 132 LowContrastSlice (class in pylinac.cbct), 42 M m (pylinac.core.geometry.line attribute), 123 MachineLog (class in pylinac.log_analyzer), 59 MachineLogs (class in pylinac.log_analyzer), 57 manufacturer (pylinac.cbct.settings attribute), 38 map_calced (pylinac.log_analyzer.fluence attribute), 71 match_points() (pylinac.starshot.linemanager method), 18 max (pylinac.core.roi.highcontrastdiskroi attribute), 133 max_error (pylinac.picketfence.picket attribute), 85 max_error (pylinac.picketfence.picketfence attribute), 82 max_error_leaf (pylinac.picketfence.picketfence attribute), 82 max_error_picket (pylinac.picketfence.picketfence attribute), 82 max_r_deviation (pylinac.vmat.segmentmanager attribute), 29 max_r_deviation (pylinac.vmat.vmat attribute), 27 median_profiles (pylinac.vmat.vmat attribute), 28 metadata (pylinac.core.image.dicomimage attribute), 120 metadata (pylinac.core.image.dicomimagestack attribute), 122 min (pylinac.core.roi.highcontrastdiskroi attribute), 133 MLC (class in pylinac.log_analyzer), 63 mlc (pylinac.log_analyzer.dlogaxisdata attribute), 68 mlc (pylinac.log_analyzer.tlogaxisdata attribute), 70 mlc_meas (pylinac.picketfence.picket attribute), 85 mlc_model (pylinac.log_analyzer.tlogheader attribute), 69 mlc_passed() (pylinac.picketfence.picket method), 85 mlc_passed_action() (pylinac.picketfence.picket method), 85 MLCMeas (class in pylinac.picketfence), 86 mm_per_pixel (pylinac.cbct.settings attribute), 39 moving_leaves (pylinac.log_analyzer.mlc attribute), 64 Index 181

mtf (pylinac.core.roi.highcontrastdiskroi attribute), 133 mtf() (pylinac.cbct.spatialresolutionslice method), 42 mu (pylinac.log_analyzer.dlogaxisdata attribute), 67 mu (pylinac.log_analyzer.tlogaxisdata attribute), 69 mu_delivered (pylinac.log_analyzer.subbeam attribute), 70 MultiProfile (class in pylinac.core.profile), 127 N next_dose_index (pylinac.log_analyzer.dlogaxisdata attribute), 67 node_center (pylinac.cbct.geodiskroi attribute), 45 nominal_length_mm (pylinac.cbct.geometricline attribute), 46 nominal_slice_thickness (pylinac.cbct.thicknessslice attribute), 43 normalize() (pylinac.core.image.imagemixin method), 119 normalize() (pylinac.core.profile.profilemixin method), 125 num_axes (pylinac.log_analyzer.tlogheader attribute), 68 num_beamholds (pylinac.log_analyzer.dlogaxisdata attribute), 68 num_beamholds (pylinac.log_analyzer.tlogaxisdata attribute), 70 num_dlogs (pylinac.log_analyzer.machinelogs attribute), 58 num_images (pylinac.cbct.settings attribute), 39 num_leaves (pylinac.log_analyzer.mlc attribute), 63 num_logs (pylinac.log_analyzer.machinelogs attribute), 58 num_mlc_leaves (pylinac.log_analyzer.dlogheader attribute), 67 num_mlc_leaves (pylinac.log_analyzer.tlogheader attribute), 68 num_moving_leaves (pylinac.log_analyzer.mlc attribute), 63 num_pairs (pylinac.log_analyzer.mlc attribute), 63 num_peaks (pylinac.cbct.spatialresolutionslice attribute), 41 num_pickets (pylinac.picketfence.picketfence attribute), 83 num_snapshots (pylinac.log_analyzer.dlogaxisdata attribute), 67 num_snapshots (pylinac.log_analyzer.mlc attribute), 63 num_snapshots (pylinac.log_analyzer.tlogheader attribute), 69 num_subbeams (pylinac.log_analyzer.tlogheader attribute), 69 num_tlogs (pylinac.log_analyzer.machinelogs attribute), 58 num_valleys (pylinac.cbct.spatialresolutionslice attribute), 41 number_large_leaves (pylinac.picketfence.settings attribute), 85 number_small_leaves (pylinac.picketfence.settings attribute), 85 O obj (AnalyzeMixin attribute), 114 obj (pylinac.watcher.analyzelog attribute), 115 obj (pylinac.watcher.analyzemixin attribute), 114 obj (pylinac.watcher.analyzepf attribute), 114 obj (pylinac.watcher.analyzestar attribute), 114 obj() (pylinac.watcher.analyzecbct method), 114 obj() (pylinac.watcher.analyzevmat method), 115 open_file() (in module pylinac.core.io), 131 open_img_is_loaded (pylinac.vmat.vmat attribute), 27 orientation (pylinac.picketfence.picketfence attribute), 84 overall_passed (pylinac.cbct.geometryslice attribute), 41 overall_passed (pylinac.cbct.huslice attribute), 40 overall_passed (pylinac.cbct.lowcontrastslice attribute), 43 overall_passed (pylinac.cbct.uniformityslice attribute), 40 Overlay (class in pylinac.picketfence), 86 P pair_moved() (pylinac.log_analyzer.mlc method), 64 pass_fail_color (pylinac.cbct.geometricline attribute), 46 pass_prcnt (pylinac.log_analyzer.gammafluence attribute), 73 passed (pylinac.cbct.geometricline attribute), 46 passed (pylinac.cbct.hudiskroi attribute), 45 passed (pylinac.cbct.thicknessslice attribute), 43 passed (pylinac.core.roi.highcontrastdiskroi attribute), 133 passed (pylinac.picketfence.mlcmeas attribute), 86 passed (pylinac.picketfence.picket attribute), 85 passed (pylinac.picketfence.picketfence attribute), 82 passed (pylinac.picketfence.pickethandler attribute), 84 passed (pylinac.starshot.starshot attribute), 17 passed (pylinac.vmat.segment attribute), 29 passed (pylinac.vmat.segmentmanager attribute), 29 passed (pylinac.vmat.vmat attribute), 27 passed_action (pylinac.picketfence.mlcmeas attribute), 86 passed_constant (pylinac.cbct.lowcontrastdiskroi attribute), 45 passed_constant (pylinac.core.roi.lowcontrastdiskroi attribute), 132 passfail_map (pylinac.log_analyzer.gammafluence attribute), 72, 73 182 Index

patient_name (pylinac.log_analyzer.dlogheader attribute), 66 peak_detect() (in module pylinac.core.profile), 130 peaks (pylinac.core.profile.multiprofile attribute), 127 penumbra_width() (pylinac.core.profile.singleprofile method), 126 percent_passing (pylinac.picketfence.picketfence attribute), 82 phan_center (pylinac.cbct.slice attribute), 40 phantom_roll (pylinac.cbct.settings attribute), 39 Picket (class in pylinac.picketfence), 85 picket_array (pylinac.picketfence.picket attribute), 85 PicketFence (class in pylinac.picketfence), 82 PicketHandler (class in pylinac.picketfence), 84 pickets (pylinac.picketfence.picketfence attribute), 82 PipsProQC3 (class in pylinac.planar_imaging), 105 pixel_array (pylinac.cbct.rectangleroi attribute), 46 pixel_array (pylinac.core.roi.rectangleroi attribute), 133 pixel_map (pylinac.log_analyzer.fluence attribute), 71 pixel_map (pylinac.log_analyzer.gammafluence attribute), 72 pixel_value (pylinac.cbct.diskroi attribute), 44 pixel_value (pylinac.core.roi.diskroi attribute), 132 plan_filename (pylinac.log_analyzer.dlogheader attribute), 66 plot() (pylinac.core.image.dicomimagestack method), 122 plot() (pylinac.core.image.imagemixin method), 118 plot() (pylinac.core.profile.multiprofile method), 127 plot() (pylinac.core.profile.singleprofile method), 127 plot() (pylinac.starshot.linemanager method), 18 plot() (pylinac.winston_lutz.wlimage method), 92 plot2axes() (pylinac.core.geometry.circle method), 123 plot2axes() (pylinac.core.geometry.line method), 124 plot2axes() (pylinac.core.geometry.rectangle method), 124 plot2axes() (pylinac.core.profile.circleprofile method), 129 plot2axes() (pylinac.core.profile.collapsedcircleprofile method), 130 plot2axes() (pylinac.picketfence.mlcmeas method), 86 plot_actual() (pylinac.log_analyzer.axis method), 63 plot_analyzed_image() (pylinac.cbct.cbct method), 36 plot_analyzed_image() (pylinac.picketfence.picketfence method), 84 plot_analyzed_image() (pylinac.planar_imaging.leedstor method), 105 plot_analyzed_image() (pylinac.planar_imaging.pipsproqc3 method), 106 plot_analyzed_image() (pylinac.starshot.starshot method), 17 plot_analyzed_image() (pylinac.vmat.vmat method), 27 plot_analyzed_subimage() (pylinac.cbct.cbct method), 37 plot_analyzed_subimage() (pylinac.starshot.starshot method), 17 plot_analyzed_subimage() (pylinac.vmat.vmat method), 28 plot_axis_images() (pylinac.winston_lutz.winstonlutz method), 91 plot_color (pylinac.cbct.hudiskroi attribute), 45 plot_color (pylinac.cbct.lowcontrastdiskroi attribute), 45 plot_color (pylinac.cbct.thicknessroi attribute), 46 plot_color (pylinac.core.roi.highcontrastdiskroi attribute), 133 plot_color (pylinac.core.roi.lowcontrastdiskroi attribute), 132 plot_color_constant (pylinac.cbct.lowcontrastdiskroi attribute), 46 plot_color_constant (pylinac.core.roi.lowcontrastdiskroi attribute), 133 plot_contrast() (pylinac.cbct.lowcontrastslice method), 43 plot_difference() (pylinac.log_analyzer.axis method), 63 plot_expected() (pylinac.log_analyzer.axis method), 63 plot_flatness() (pylinac.flatsym.beamimage method), 111 plot_flatsym() (pylinac.flatsym.beamimage method), 110 plot_gantry_sag() (pylinac.winston_lutz.winstonlutz method), 91 plot_histogram() (pylinac.log_analyzer.gammafluence method), 74 plot_images() (pylinac.winston_lutz.winstonlutz method), 91 plot_linearity() (pylinac.cbct.huslice method), 40 plot_lines() (pylinac.cbct.geometryslice method), 41 plot_map() (pylinac.log_analyzer.fluence method), 72 plot_map() (pylinac.log_analyzer.gammafluence method), 73 plot_mlc_error_hist() (pylinac.log_analyzer.mlc method), 66 plot_mtf() (pylinac.cbct.spatialresolutionslice method), 42 plot_passfail_map() (pylinac.log_analyzer.gammafluence method), 74 plot_profiles() (pylinac.cbct.uniformityslice method), 40 plot_rms_by_leaf() (pylinac.log_analyzer.mlc method), 66 plot_rois() (pylinac.cbct.lowcontrastslice method), 43 plot_rois() (pylinac.cbct.roimanagermixin method), 44 plot_rois() (pylinac.cbct.spatialresolutionslice method), 42 plot_summary() (pylinac.log_analyzer.machinelog method), 61 plot_summary() (pylinac.winston_lutz.winstonlutz method), 91 Index 183

plot_symmetry() (pylinac.flatsym.beamimage method), 111 Point (class in pylinac.core.geometry), 122 previous_dose_index (pylinac.log_analyzer.dlogaxisdata attribute), 67 previous_segment_num (pylinac.log_analyzer.dlogaxisdata attribute), 67 profile_peaks_idxs (pylinac.cbct.spatialresolutionslice attribute), 42 profile_peaks_vals (pylinac.cbct.spatialresolutionslice attribute), 42 profile_valleys_idxs (pylinac.cbct.spatialresolutionslice attribute), 42 profile_valleys_vals (pylinac.cbct.spatialresolutionslice attribute), 42 ProfileMixin (class in pylinac.core.profile), 125 pylinac.cbct (module), 31 pylinac.core.decorators (module), 134 pylinac.core.geometry (module), 122 pylinac.core.image (module), 117 pylinac.core.io (module), 131 pylinac.core.mask (module), 133 pylinac.core.profile (module), 124 pylinac.core.roi (module), 132 pylinac.core.utilities (module), 134 pylinac.log_analyzer (module), 47 pylinac.picketfence (module), 75 pylinac.planar_imaging (module), 95 pylinac.starshot (module), 11 pylinac.vmat (module), 21 pylinac.winston_lutz (module), 87 R r_corr (pylinac.vmat.segment attribute), 29 r_dev (pylinac.vmat.segment attribute), 29 r_devs (pylinac.vmat.segmentmanager attribute), 29 rad_time (pylinac.log_analyzer.subbeam attribute), 70 radius2linepairs (pylinac.cbct.spatialresolutionslice attribute), 42 radius2linepairs_mm (pylinac.cbct.spatialresolutionslice attribute), 41 radius_mm (pylinac.starshot.wobble attribute), 18 Rectangle (class in pylinac.core.geometry), 124 RectangleROI (class in pylinac.cbct), 46 RectangleROI (class in pylinac.core.roi), 133 remove_edges() method), 118 report_basic_parameters() (pylinac.core.image.imagemixin (pylinac.log_analyzer.machinelog method), 61 report_basic_parameters() (pylinac.log_analyzer.machinelogs method), 58 resize() (pylinac.core.image.imagemixin method), 119 resolution (pylinac.log_analyzer.fluence attribute), 71 results() (pylinac.winston_lutz.winstonlutz method), 92 return_results() (pylinac.cbct.cbct method), 38 return_results() (pylinac.picketfence.picketfence method), 84 return_results() (pylinac.starshot.starshot method), 17 return_results() (pylinac.vmat.vmat method), 28 right_guard (pylinac.picketfence.picket attribute), 86 roi_angles (pylinac.cbct.roimanagermixin attribute), 44 roi_background_names (pylinac.cbct.lowcontrastslice attribute), 42 roi_heights (pylinac.cbct.thicknessslice attribute), 43 roi_heights_mm (pylinac.cbct.thicknessslice attribute), 43 roi_names (pylinac.cbct.roimanagermixin attribute), 44 roi_nominal_angles (pylinac.cbct.roimanagermixin attribute), 44 roi_nominal_values (pylinac.cbct.huslice attribute), 40 roi_radius (pylinac.cbct.lowcontrastslice attribute), 43 roi_radius (pylinac.cbct.roimanagermixin attribute), 44 roi_radius_mm (pylinac.cbct.roimanagermixin attribute), 44 roi_widths (pylinac.cbct.thicknessslice attribute), 43 roi_widths_mm (pylinac.cbct.thicknessslice attribute), 43 ROIManagerMixin (class in pylinac.cbct), 43 rois_visible (pylinac.cbct.lowcontrastslice attribute), 43 roll() (pylinac.core.profile.circleprofile method), 129 rot90() (pylinac.core.image.imagemixin method), 119 rotn (pylinac.log_analyzer.couchstruct attribute), 74 run_demo() (pylinac.cbct.cbct static method), 38 run_demo() (pylinac.flatsym.beamimage method), 110 run_demo() (pylinac.picketfence.picketfence static method), 83 run_demo() (pylinac.planar_imaging.leedstor static method), 104 run_demo() (pylinac.planar_imaging.pipsproqc3 static method), 105 run_demo() (pylinac.starshot.starshot static method), 18 run_demo() (pylinac.winston_lutz.winstonlutz static method), 90 run_demo_drgs() (pylinac.vmat.vmat static method), 27 run_demo_drmlc() (pylinac.vmat.vmat static method), 27 run_dlog_demo() (pylinac.log_analyzer.machinelog static method), 60 run_tlog_demo() (pylinac.log_analyzer.machinelog static method), 60 S sample_width (pylinac.picketfence.picket attribute), 85 samples_per_axis (pylinac.log_analyzer.tlogheader attribute), 68 184 Index

sampling_interval (pylinac.log_analyzer.tlogheader attribute), 68 slice_num (pylinac.cbct.huslice attribute), 40 slice_num (pylinac.cbct.geometryslice attribute), 41 save_analyzed_image() (pylinac.cbct.cbct method), 37 slice_num (pylinac.cbct.spatialresolutionslice attribute), save_analyzed_image() (pylinac.picketfence.picketfence 41 method), 84 slice_num (pylinac.cbct.thicknessslice attribute), 43 save_analyzed_image() (pylinac.planar_imaging.leedstorslice_num (pylinac.cbct.uniformityslice attribute), 40 method), 105 small_leaf_width (pylinac.picketfence.settings attribute), save_analyzed_image() (pylinac.planar_imaging.pipsproqc3 method), 106 save_analyzed_image() (pylinac.starshot.starshot method), 17 save_analyzed_image() (pylinac.vmat.vmat method), 27 save_analyzed_subimage() (pylinac.cbct.cbct method), 37 save_analyzed_subimage() (pylinac.starshot.starshot method), 17 save_analyzed_subimage() (pylinac.vmat.vmat method), 28 save_histogram() (pylinac.log_analyzer.gammafluence method), 74 save_image() (pylinac.watcher.analyzemixin method), 114 save_images() (pylinac.winston_lutz.winstonlutz method), 91 save_map() (pylinac.log_analyzer.fluence method), 72 save_mlc_error_hist() (pylinac.log_analyzer.mlc method), 66 save_plot() (pylinac.winston_lutz.wlimage method), 92 save_rms_by_leaf() (pylinac.log_analyzer.mlc method), 66 save_summary() (pylinac.log_analyzer.machinelog method), 61 save_summary() (pylinac.winston_lutz.winstonlutz method), 91 save_text() (pylinac.watcher.analyzemixin method), 114 sector_mask() (in module pylinac.core.mask), 133 Segment (class in pylinac.vmat), 29 SegmentManager (class in pylinac.vmat), 28 segments (pylinac.vmat.vmat attribute), 25 sequence_num (pylinac.log_analyzer.subbeam attribute), 70 Settings (class in pylinac.cbct), 38 Settings (class in pylinac.picketfence), 85 Settings (class in pylinac.vmat), 28 settings (pylinac.cbct.cbct attribute), 35 settings (pylinac.vmat.vmat attribute), 25 sid (pylinac.core.image.dicomimage attribute), 120 sid (pylinac.core.image.fileimage attribute), 120 simple_round() (in module pylinac.core.utilities), 134 sin() (in module pylinac.core.geometry), 122 SingleProfile (class in pylinac.core.profile), 125 size (pylinac.core.profile.circleprofile attribute), 129 Slice (class in pylinac.cbct), 39 85 spaced_circle_profile (pylinac.cbct.spatialresolutionslice attribute), 42 spatialres (pylinac.cbct.cbct attribute), 35 SpatialResolutionSlice (class in pylinac.cbct), 41 square_ratio() (in module pylinac.core.mask), 133 sr_slice_num (pylinac.cbct.settings attribute), 39 StarProfile (class in pylinac.starshot), 18 Starshot (class in pylinac.starshot), 14 start_angle (pylinac.core.profile.circleprofile attribute), 129 std (pylinac.cbct.diskroi attribute), 44 std (pylinac.core.roi.diskroi attribute), 132 stretch() (in module pylinac.core.profile), 124 stretch() (pylinac.core.profile.profilemixin method), 125 Subbeam (class in pylinac.log_analyzer), 70 subbeams (pylinac.log_analyzer.machinelog attribute), 60 subdivide() (pylinac.core.profile.multiprofile method), 128 symmetry() (pylinac.flatsym.beamimage method), 111 T tan() (in module pylinac.core.geometry), 122 TemporaryZipDirectory (class in pylinac.core.io), 131 TemporaryZipURLDirectory (class in pylinac.core.io), 131 test_type (pylinac.vmat.settings attribute), 28 thickness (pylinac.cbct.cbct attribute), 35 ThicknessROI (class in pylinac.cbct), 46 ThicknessSlice (class in pylinac.cbct), 43 threshold (pylinac.cbct.settings attribute), 38 threshold (pylinac.log_analyzer.gammafluence attribute), 73 threshold() (pylinac.core.image.imagemixin method), 119 timethis() (in module pylinac.core.decorators), 134 TlogAxisData (class in pylinac.log_analyzer), 69 TlogHeader (class in pylinac.log_analyzer), 68 to_csv() (pylinac.log_analyzer.machinelog method), 62 to_csv() (pylinac.log_analyzer.machinelogs method), 58 Tolerance (class in pylinac.starshot), 18 tolerance (pylinac.cbct.geometryslice attribute), 41 tolerance (pylinac.cbct.huslice attribute), 40 tolerance (pylinac.cbct.thicknessslice attribute), 43 tolerance (pylinac.cbct.uniformityslice attribute), 40 tolerance (pylinac.log_analyzer.dlogheader attribute), 67 Index 185

tolerance (pylinac.starshot.starshot attribute), 15 tolerance (pylinac.vmat.settings attribute), 28 treatment_type (pylinac.log_analyzer.machinelog attribute), 62 txt (pylinac.log_analyzer.machinelog attribute), 60 txt_filename (pylinac.watcher.analyzemixin attribute), 114 type_accept() (in module pylinac.core.decorators), 134 typed_property() (in module pylinac.core.utilities), 134 U un_slice_num (pylinac.cbct.settings attribute), 39 uniformity (pylinac.cbct.cbct attribute), 35 UniformitySlice (class in pylinac.cbct), 40 V valleys (pylinac.core.profile.multiprofile attribute), 127 value_accept() (in module pylinac.core.decorators), 135 value_diff (pylinac.cbct.hudiskroi attribute), 45 values (pylinac.core.profile.multiprofile attribute), 127 values (pylinac.core.profile.singleprofile attribute), 126 variable_axis (pylinac.winston_lutz.wlimage attribute), 93 Vector (class in pylinac.core.geometry), 122 vector_is_close() (in module pylinac.core.geometry), 122 version (pylinac.log_analyzer.dlogheader attribute), 66 version (pylinac.log_analyzer.tlogheader attribute), 68 vert (pylinac.log_analyzer.couchstruct attribute), 74 VMAT (class in pylinac.vmat), 25 W WinstonLutz (class in pylinac.winston_lutz), 90 wire_fwhm (pylinac.cbct.thicknessroi attribute), 46 WLImage (class in pylinac.winston_lutz), 92 Wobble (class in pylinac.starshot), 18 wobble (pylinac.starshot.starshot attribute), 15 X x() (pylinac.core.geometry.line method), 124 x1 (pylinac.log_analyzer.jawstruct attribute), 74 x2 (pylinac.log_analyzer.jawstruct attribute), 74 x_locations (pylinac.core.profile.circleprofile attribute), 129 x_offset (pylinac.vmat.settings attribute), 28 x_offset (pylinac.winston_lutz.wlimage attribute), 92 Y y() (pylinac.core.geometry.line method), 124 y1 (pylinac.log_analyzer.jawstruct attribute), 74 y2 (pylinac.log_analyzer.jawstruct attribute), 74 y_locations (pylinac.core.profile.circleprofile attribute), 129 y_offset (pylinac.vmat.settings attribute), 28 y_offset (pylinac.winston_lutz.wlimage attribute), 92 Z z_offset (pylinac.winston_lutz.wlimage attribute), 92 186 Index