Flatness/Symmetry module documentation¶
Overview¶
The Flatness & Symmetry module (pylinac.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. There is one main class FlatSym
. If you will be using
custom algorithms there are two module dictionaries you will need: FLATNESS_EQUATIONS
and SYMMETRY_EQUATIONS
.
Running the Demo¶
To run the demo, import the main class and run the demo method:
from pylinac import FlatSym
FlatSym.run_demo()
(Source code, png, hires.png, pdf)
Which will also result in the following output:
Flatness & Symmetry
File: C:\Users\jkern\...inac\pylinac\demo_files\flatsym_demo.dcm
Flatness method: Varian
Vertical flatness: 1.931%
Horizontal flatness: 1.857%
Symmetry method: Varian
Vertical symmetry: 2.462%
Horizontal symmetry: 2.989%
Penumbra (80/20):
Horizontal: 2.6mm
Vertical: 2.8mm
Field Size:
Horizontal: 140.9mm
Vertical: 200.2mm
CAX to edge distances:
CAX > Upper edge: 99.9mm
CAX > Lower edge: 100.4mm
CAX > Left edge: 60.4mm
CAX > Right edge: 80.6mm
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 FlatSym
class:
from pylinac import FlatSym
Loading images is easy and just like any other module:
# from a file
my_file = r"C:/my/QA/folder/img.dcm"
my_img = FlatSym(path=my_file)
If you don’t have an image you can load the demo image:
my_img = FlatSym.from_demo_image()
You can then calculate the flatness and symmetry with the analyze()
method:
my_img.analyze(flatness_method='varian', symmetry_method='varian', vert_position=0.5, horiz_position=0.5)
After analysis, the results can be printed, plotted, or saved to a PDF:
print(my_img.results()) # print results
my_img.plot_analyzed_image() # matplotlib image
my_img.publish_pdf(filename="flatsym.pdf") # create PDF and save to file
Raw Data¶
The raw data values of analysis are also available within the public attributes of the class:
my_img = FlatSym.from_demo_image()
my_img.analyze(flatness_method='varian', symmetry_method='varian')
my_img.symmetry['horizontal']['value'] # the actual symmetry value
my_img.flatness['vertical']['value']
Analysis Options¶
The flatness/symmetry algorithms can be specified as well as the position of the analysis within the image and the width of the profile. See Analysis Definitions for the common algorithms.
my_img.analyze(flatness_method='elekta', symmetry_method='point difference', vert_position=0.4, horiz_position=0.6,
vert_width=0.05, horiz_width=0.05)
You can also create your own algorithms.
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 builtin definitions which you can use.
Symmetry:
Name  Parameter values  Vendors  Equation 

Point Difference  varian , 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 , pdq iec 
Elekta  \(100 * max(L_{pt}/R_{pt}, R_{pt}/L_{pt})\) over 80%FW if 10<FW<30cm [1] 
 – Parameter value(s), Name, Vendors that use it – Equation
 –
varian
,point difference
, 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.  –
elekta
,pdq iec
, Point Difference Quotient (IEC), Elekta – \(100 * max(L_{pt}/R_{pt}, R_{pt}/L_{pt})\) over 80%FW if 10<FW<30cm [1].
Flatness:
 – Parameter value(s), Name, Vendors that use it – Equation
 –
varian
,vom80
,siemens
, Variation over mean (80%), Varian – \(100 * D_{max}  D_{min} / (D_{max} + D_{min})\) within 80%FW.  –
elekta
,iec
, 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.
[1]  (1, 2, 3) 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. 
Creating & Using Custom Algorithms¶
Custom Algorithm Structure¶
The flatness/symmetry algorithms can easily be extended. All algorithms are in a module dictionary, so the required steps are to 1) create the custom analysis function and 2) add it to the equation dictionary.
The custom algorithms must be functions and follow the below structure. All names can be changed, but the structure of one input and correct number and order of outputs is fixed:
def custom_flatness(profile: SingleProfile) > Sequence[float]:
...
return flatness_value, max_value, min_value, left_edge_index, right_edge_index
def custom_symmetry(profile: SingleProfile) > Tuple[float, Sequence[float], float, float]:
...
return symmetry_value, symmetry_array, left_edge, right_edge
While most values are easily understood the symmetry_array
should be a list of floats or numpy array of the symmetry value at each point,
assuming to start at the CAX and move outward.
Note
When creating custom algorithms, use the above for guidance. It 1) must be a function and
2) must have one input argument, which will be a SingleProfile
.
Depending on whether it is a flatness or symmetry calculation the return values must follow the above
structure. For further examples, see the source code for the builtin equations;
e.g. flatness_varian()
.
Note
The SingleProfile
given to the function is very powerful and can calculate
numerous helpful data for you such as the field edges, minimum/maximum value within the field, and much more.
Read the documentation before creating a custom algorithm.
Note
For flatness equations, if there is no max_value
or min_value
(e.g. area calculations) then just set
those values to 0
. Additionally, if you do not want or have a symmetry_array
to plot, then pass 0
and
it will not be plotted.
Using Custom Algorithms¶
To use the custom algorithm we must now add it to the equation dictionary using a custom name. The name can be anything you like that is a valid dictionary key:
from pylinac import FlatSym
from pylinac.flatsym import SYMMETRY_EQUATIONS, FLATNESS_EQUATIONS
def custom_flatness(profile: SingleProfile):
...
FLATNESS_EQUATIONS['my custom flatness'] = custom_flatness # no parentheses; we don't want to call it, just assign it
my_img = FlatSym.from_demo_image()
my_img.analyze(flatness_method='my custom flatness', ...) # use whatever key you defined in the equation dict
...
Algorithm¶
There is little of a true “algorithm” in flatsym
other analyzing profiles. 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).
 Analysis is limited to normal/parallel directions. Thus if the image is rotated there is no way to account for it other than rotating the image before analysis.
Analysis
 Extract profiles  With the positions given, profiles 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.
API Documentation¶

class
pylinac.flatsym.
FlatSym
(path: str, filter: Optional[int] = None)[source]¶ Class for analyzing the flatness and symmetry of a radiation image, most commonly an open image from a linac.

symmetry
¶ Contains the method of calculation and the vertical and horizontal symmetry data including the value.
Type: dict

flatness
¶ Contains the method of calculation and the vertical and horizontal flatness data including the value.
Type: dict

positions
¶ The position ratio used for analysis for vertical and horizontal.
Type: dict

widths
¶ The width ratios used for analysis for vertical and horizontal.
Type: dict
Parameters:  path (str) – The path to the image.
 filter (None or int) – If None, no filter is applied. If an int, a median filter of size n pixels is applied. Generally, a good idea. Default is None for backwards compatibility.

static
run_demo
()[source]¶ Run the Flat/Sym demo by loading the demo image, print results, and plot the profiles.

analyze
(flatness_method: str, symmetry_method: str, vert_position: float = 0.5, horiz_position: float = 0.5, vert_width: Union[float, int] = 0, horiz_width: Union[float, int] = 0, invert: bool = False)[source]¶ Analyze the image to determine flatness & symmetry.
Parameters:  flatness_method ({'varian', 'elekta', 'vom80', 'siemens', 'iec'}) – The flatness algorithm. See Analysis Definitions for equations.
 symmetry_method ({'varian', 'elekta', 'point difference', 'pdq iec'}) – The symmetry algorithm. See Analysis Definitions for equations.
 vert_position (float (0.01.0)) – The distance ratio of the image to sample. E.g. at the default of 0.5 the profile is extracted in the middle of the image. 0.0 is at the left edge of the image and 1.0 is at the right edge of the image.
 horiz_position (float (0.01.0)) – The distance ratio of the image to sample. E.g. at the default of 0.5 the profile is extracted in the middle of the image. 0.0 is at the top edge of the image and 1.0 is at the bottom edge of the image.
 vert_width (float (0.01.0)) – The width ratio of the image to sample. E.g. at the default of 0.0 a 1 pixel wide profile is extracted. 0.0 would be 1 pixel wide and 1.0 would be the vertical image width.
 horiz_width (float (0.01.0)) – The width ratio of the image to sample. E.g. at the default of 0.0 a 1 pixel wide profile is extracted. 0.0 would be 1 pixel wide and 1.0 would be the horizontal image width.
 invert (bool) – Whether to invert the image. Setting this to True will override the default inversion. This is useful if pylinac’s automatic inversion is incorrect.

results
(as_str: bool = True) → Union[str, list][source]¶ Get the results of the analysis.
Parameters: as_str (bool) – If True, return a single string. If False, return a list. Useful for PDF publishing. Returns: results Return type: str or list

publish_pdf
(filename: str, notes: Union[str, list] = None, open_file: bool = False, metadata: dict = None)[source]¶ Publish (print) a PDF containing the analysis, images, and quantitative results.
Parameters:  filename ((str, filelike object}) – The file to write the results to.
 notes (str, list of strings) – Text; if str, prints single line. If list of strings, each list item is printed on its own line.
 open_file (bool) – Whether to open the file using the default program after creation.
 metadata (dict) – Extra data to be passed and shown in the PDF. The key and value will be shown with a colon. E.g. passing {‘Author’: ‘James’, ‘Unit’: ‘TrueBeam’} would result in text in the PDF like: ————– Author: James Unit: TrueBeam ————–


pylinac.flatsym.
flatness_varian
(profile: pylinac.core.profile.SingleProfile) → Sequence[float][source]¶ The Varian specification for calculating flatness

pylinac.flatsym.
flatness_elekta
(profile: pylinac.core.profile.SingleProfile) → Sequence[float][source]¶ The Elekta specification for calculating flatness