# Winston-Lutz module documentation¶

## 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.
• BB shift instructions - Direct shift instructions can be printed for iterative BB placement. The current couch position can even be input to get the new couch values.
• Axis deviation plots - Plot the variation of the gantry, collimator, couch, and EPID in each plane as well as RMS variation.
• File name interpretation - Rename DICOM filenames to include axis information for linacs that don’t include such information in the DICOM tags. E.g. “myWL_gantry45_coll0_couch315.dcm”.

## 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.14mm
Median 2D CAX->BB distance: 0.64mm
Shift BB to iso, facing gantry: LEFT 0.02mm; DOWN 0.11mm; OUT 0.29mm
Gantry 3D isocenter diameter: 0.97mm
Maximum Gantry RMS deviation (mm): 0.99mm
Maximum EPID RMS deviation (mm): 0.00mm
Collimator 2D isocenter diameter: 1.12mm
Maximum Collimator RMS deviation (mm): 0.87
Couch 2D isocenter diameter: 1.13mm
Maximum Couch RMS deviation (mm): 1.14


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

## 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 - Gun-Target, or in-out, with out/Target being positive.

## 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)


You can also load a ZIP archive with the images in it:

wl = WinstonLutz.from_zip('path/to/wl.zip')


And that’s it! Once loaded you can view images, print the results, or publish a PDF report:

# plot all the images
wl.plot_images()
# plot an individual image
wl.images[3].plot()
# save a figure of the image plots
wl.save_plots('wltest.png')
# print to PDF
wl.publish_pdf('mywl.pdf')


If you want to shift the BB based on the results and perform the test again there is a method for that:

print(wl.bb_shift_instructions())
# LEFT: 0.1mm, DOWN: 0.22mm, ...


You can also pass in your couch coordinates and the new values will be generated:

print(wl.bb_shift_instructions(couch_vrt=0.41, couch_lng=96.23, couch_lat=0.12))
New couch coordinates (mm): VRT: 0.32; LNG: 96.11; LAT: 0.11


## Using File Names¶

If your linac EPID images do not include axis information (such as Elekta) you can specify it in the file name. Any and all of the three axes can be defined. If one is not defined and is not in the DICOM tags, it will default to 0. The syntax to define the axes: “gantry0*coll0*couch0”. There can be any text before, after, or in between each axis definition. However, the axes numerical value must immediately follow the axis name. Axis names are also fixed. The following examples are valid:

• MyWL-gantry0-coll90-couch315.dcm
• gantry90_stuff_coll45-couch0.dcm
• abc-couch45-gantry315-coll0.dcm
• 01-gantry0-abcd-coll30couch10abc.dcm
• abc-gantry30.dcm
• coll45abc.dcm

The following are invalid:

• mywl-gantry=0-coll=90-couch=315.dcm
• gan45_collimator30-table270.dcm

Note

If using filenames any relevant axes must be defined, otherwise they will default to zero. For example, if the acquisition was at gantry=45, coll=15, couch=0 then the filename must include both the gantry and collimator in the name (<…gantry45…coll15….dcm>). For this example, the couch need not be defined since it is 0.

## 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 BB must be within 2.5cm of the real isocenter.
• 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.

• 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 couch was 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.

## API Documentation¶

class pylinac.winston_lutz.WinstonLutz(directory, use_filenames=False)[source]

Bases: object

Class for performing a Winston-Lutz test of the radiation isocenter.

Parameters: directory (str) – Path to the directory of the Winston-Lutz EPID images. use_filenames (bool) – Whether to try to use the file name to determine axis values. Useful for Elekta machines that do not include that info in the DICOM data.

Examples

Run the demo:

>>> WinstonLutz.run_demo()


Load a directory with Winston-Lutz EPID images:

>>> wl = WinstonLutz('path/to/directory')


>>> wl = WinstonLutz.from_zip('path/to/images.zip')


Or use the demo images provided:

>>> wl = WinstonLutz.from_demo_images()

images
Type: ImageManager instance
classmethod from_demo_images()[source]

Instantiate using the demo images.

classmethod from_zip(zfile, use_filenames=False)[source]

Instantiate from a zip file rather than a directory.

Parameters: zfile (str) – Path to the archive file. use_filenames (bool) – Whether to interpret axis angles using the filenames. Set to true for Elekta machines where the gantry/coll/couch data is not in the DICOM metadata.
classmethod from_url(url, use_filenames=False)[source]

Instantiate from a URL.

Parameters: url (str) – URL that points to a zip archive of the DICOM images. use_filenames (bool) – Whether to interpret axis angles using the filenames. Set to true for Elekta machines where the gantry/coll/couch data is not in the DICOM metadata.
static run_demo()[source]

Run the Winston-Lutz demo, which loads the demo files, prints results, and plots a summary image.

gantry_iso_size

The diameter of the 3D gantry isocenter size in mm. Only images where the collimator and couch were at 0 are used to determine this value.

collimator_iso_size

The 2D collimator isocenter size (diameter) in mm. The iso size is in the plane normal to the gantry.

couch_iso_size

The diameter of the 2D couch isocenter size in mm. Only images where the gantry and collimator were at zero are used to determine this value.

bb_shift_vector

The shift necessary to place the BB at the radiation isocenter

bb_shift_instructions(couch_vrt=None, couch_lng=None, couch_lat=None)[source]

A string describing how to shift the BB to the radiation isocenter

axis_rms_deviation(axis='Gantry', value='all')[source]

The RMS deviations of a given axis/axes.

Parameters: axis (('Gantry', 'Collimator', 'Couch', 'Epid', 'Combo'}) – The axis desired. value ({'all', 'range'}) – Whether to return all the RMS values from all images for that axis, or only return the maximum range of values, i.e. the ‘sag’.
cax2bb_distance(metric='max')[source]

The distance in mm between the CAX and BB for all images according to the given metric.

Parameters: metric ({'max', 'median'}) – The metric of distance to use.
cax2epid_distance(metric='max')[source]

The distance in mm between the CAX and EPID center pixel for all images according to the given metric.

Parameters: metric ({'max', 'median'}) – The metric of distance to use.
plot_axis_images(axis='Gantry', show=True, ax=None)[source]

Plot all CAX/BB/EPID 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.

Parameters: 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(axis='All', show=True)[source]

Plot a grid of all the images acquired.

Four columns are plotted with the titles showing which axis that column represents.

Parameters: axis ({'Gantry', 'Collimator', 'Couch', 'Combo', 'All'}) – show (bool) – Whether to show the image.
save_images(filename, axis='All', **kwargs)[source]

Save the figure of plot_images() to file. Keyword arguments are passed to matplotlib.pyplot.savefig().

Parameters: filename (str) – The name of the file to save to.
plot_summary(show=True)[source]

Plot a summary figure showing the gantry sag and wobble plots of the three axes.

save_summary(filename, **kwargs)[source]

Save the summary image.

results()[source]

Return the analysis results summary.

publish_pdf(filename, unit=None, notes=None, open_file=False)[source]

Publish (print) a PDF containing the analysis and quantitative results.

Parameters: filename ((str, file-like object}) – The file to write the results to. unit (str) – The name of the unit the data was acquired on; e.g. “TrueBeam 1”. notes ((None, str)) – An arbitrary string of information that may be useful to include in the PDF report. open_file (bool) – Whether to open the PDF file after publishing.
class pylinac.winston_lutz.ImageManager(directory, use_filenames)[source]

Bases: list

Manages the images of a Winston-Lutz test.

Parameters: directory (str) – The path to the images. use_filenames (bool) – Whether to try to use the file name to determine axis values. Useful for Elekta machines that do not include that info in the DICOM data.
class pylinac.winston_lutz.WLImage(file, use_filenames)[source]

Holds individual Winston-Lutz EPID images, image properties, and automatically finds the field CAX and BB.

Parameters: file (str) – Path to the image file. use_filenames (bool) – Whether to try to use the file name to determine axis values. Useful for Elekta machines that do not include that info in the DICOM data.
epid

Center of the EPID panel

epid_y_offset

The offset or distance between the field CAX and EPID in the y-direction (AP).

bb_y_offset

The offset or distance between the field CAX and BB in the y-direction (AP).

epid_x_offset

The offset or distance between the field CAX and EPID in the x-direction (LR).

bb_x_offset

The offset or distance between the field CAX and BB in the x-direction (LR).

epid_z_offset

The offset or distance between the field CAX and EPID in z-direction (SI).

bb_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. Line
cax2bb_vector

The vector in mm from the CAX to the BB.

cax2bb_vector3d

The vector in mm from the CAX to the BB.

cax2epid_vector

The vector in mm from the CAX to the EPID center pixel

cax2bb_distance

The scalar distance in mm from the CAX to the BB.

cax2epid_distance

The scalar distance in mm from the CAX to the EPID center pixel

plot(ax=None, show=True, clear_fig=False)[source]

Plot the image, zoomed-in on the radiation field, along with the detected BB location and field CAX location.

Parameters: 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.
save_plot(filename, **kwargs)[source]

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.