Skip to content

Commit

Permalink
Merged in clean_up_image_generator (pull request #370)
Browse files Browse the repository at this point in the history
cleans up image generator issues

Approved-by: Randy Taylor
  • Loading branch information
jrkerns committed Apr 8, 2024
2 parents 32af3df + b4d2c0c commit 8eb19fa
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 56 deletions.
5 changes: 5 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import sys
import warnings

import matplotlib.pyplot as plt

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
Expand All @@ -25,6 +27,9 @@

# suppress pydicom VR length warnings; just creates noise


plt.set_cmap("gray")

warnings.filterwarnings("ignore", category=UserWarning, module="pydicom")

# -- General configuration ------------------------------------------------
Expand Down
54 changes: 23 additions & 31 deletions docs/source/image_generator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ The basics to get started are to import the image simulators and layers from pyl

from matplotlib import pyplot as plt

from pylinac.core.image_generator import AS1000Image
from pylinac.core.image_generator import AS1200Image
from pylinac.core.image_generator.layers import FilteredFieldLayer, GaussianFilterLayer

as1000 = AS1000Image() # this will set the pixel size and shape automatically
as1000.add_layer(FilteredFieldLayer(field_size_mm=(50, 50))) # create a 50x50mm square field
as1000.add_layer(GaussianFilterLayer(sigma_mm=2)) # add an image-wide gaussian to simulate penumbra/scatter
as1000.generate_dicom(file_out_name="my_AS1000.dcm", gantry_angle=45) # create a DICOM file with the simulated image
as1200 = AS1200Image() # this will set the pixel size and shape automatically
as1200.add_layer(FilteredFieldLayer(field_size_mm=(50, 50))) # create a 50x50mm square field
as1200.add_layer(GaussianFilterLayer(sigma_mm=2)) # add an image-wide gaussian to simulate penumbra/scatter
as1200.generate_dicom(file_out_name="my_AS1200.dcm", gantry_angle=45) # create a DICOM file with the simulated image
# plot the generated image
plt.imshow(as1000.image)
plt.imshow(as1200.image)

Layers & Simulators
-------------------
Expand Down Expand Up @@ -81,10 +81,10 @@ To implement a custom layer, inherit from ``Layer`` and implement the ``apply``
# use
from pylinac.core.image_generator import AS1000Image
from pylinac.core.image_generator import AS1200Image
as1000 = AS1000Image()
as1000.add_layer(MyAwesomeLayer())
as1200 = AS1200Image()
as1200.add_layer(MyAwesomeLayer())
...
Examples
Expand All @@ -98,29 +98,29 @@ Simple Open Field
.. plot::

from matplotlib import pyplot as plt
from pylinac.core.image_generator import AS1000Image
from pylinac.core.image_generator import AS1200Image
from pylinac.core.image_generator.layers import FilteredFieldLayer, GaussianFilterLayer

as1000 = AS1000Image() # this will set the pixel size and shape automatically
as1000.add_layer(FilteredFieldLayer(field_size_mm=(150, 150))) # create a 50x50mm square field
as1000.add_layer(GaussianFilterLayer(sigma_mm=2)) # add an image-wide gaussian to simulate penumbra/scatter
as1200 = AS1200Image() # this will set the pixel size and shape automatically
as1200.add_layer(FilteredFieldLayer(field_size_mm=(150, 150))) # create a 50x50mm square field
as1200.add_layer(GaussianFilterLayer(sigma_mm=2)) # add an image-wide gaussian to simulate penumbra/scatter
# plot the generated image
plt.imshow(as1000.image)
plt.imshow(as1200.image)

Off-center Open Field
^^^^^^^^^^^^^^^^^^^^^

.. plot::

from matplotlib import pyplot as plt
from pylinac.core.image_generator import AS1000Image
from pylinac.core.image_generator import AS1200Image
from pylinac.core.image_generator.layers import FilteredFieldLayer, GaussianFilterLayer

as1000 = AS1000Image() # this will set the pixel size and shape automatically
as1000.add_layer(FilteredFieldLayer(field_size_mm=(30, 30), cax_offset_mm=(20, 40)))
as1000.add_layer(GaussianFilterLayer(sigma_mm=3))
as1200 = AS1200Image() # this will set the pixel size and shape automatically
as1200.add_layer(FilteredFieldLayer(field_size_mm=(30, 30), cax_offset_mm=(20, 40)))
as1200.add_layer(GaussianFilterLayer(sigma_mm=3))
# plot the generated image
plt.imshow(as1000.image)
plt.imshow(as1200.image)

Winston-Lutz FFF Cone Field with Noise
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -194,17 +194,9 @@ rotates the image after every layer is applied.
from pylinac.core.image_generator.layers import FilteredFieldLayer, GaussianFilterLayer

as1200 = AS1200Image()
as1200.add_layer(FilteredFieldLayer((250, 7), alpha=0.5))
as1200.image = ndimage.rotate(as1200.image, 30, reshape=False, mode='nearest')
as1200.add_layer(FilteredFieldLayer((250, 7), alpha=0.5))
as1200.image = ndimage.rotate(as1200.image, 30, reshape=False, mode='nearest')
as1200.add_layer(FilteredFieldLayer((250, 7), alpha=0.5))
as1200.image = ndimage.rotate(as1200.image, 30, reshape=False, mode='nearest')
as1200.add_layer(FilteredFieldLayer((250, 7), alpha=0.5))
as1200.image = ndimage.rotate(as1200.image, 30, reshape=False, mode='nearest')
as1200.add_layer(FilteredFieldLayer((250, 7), alpha=0.5))
as1200.image = ndimage.rotate(as1200.image, 30, reshape=False, mode='nearest')
as1200.add_layer(FilteredFieldLayer((250, 7), alpha=0.5))
for _ in range(6):
as1200.add_layer(FilteredFieldLayer((250, 7), alpha=0.5))
as1200.image = ndimage.rotate(as1200.image, 30, reshape=False, mode='nearest')
as1200.add_layer(GaussianFilterLayer())
plt.imshow(as1200.image)
plt.show()
Expand All @@ -219,7 +211,7 @@ Using the new utility functions of v2.5+ we can construct full dicom files of pi
from pylinac.core.image_generator import generate_picketfence, generate_winstonlutz
from pylinac.core import image_generator
sim = image_generator.simulators.AS1000Image()
sim = image_generator.simulators.AS1200Image()
field_layer = image_generator.layers.FilteredFieldLayer # could also do FilterFreeLayer
generate_picketfence(
simulator=Simulator,
Expand Down
20 changes: 10 additions & 10 deletions docs/source/vmat_docs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,15 @@ In this example, we generate a perfectly flat set of images and analyze them.
# open image
open_path = 'perfect_open_drmlc.dcm'
as1200 = AS1200Image()
as1200.add_layer(PerfectFieldLayer(field_size_mm=(150, 110), cax_offset_mm=(0, 5)))
as1200.add_layer(PerfectFieldLayer(field_size_mm=(150, 85), cax_offset_mm=(0, 0)))
as1200.add_layer(GaussianFilterLayer(sigma_mm=2))
as1200.generate_dicom(file_out_name=open_path)

# DMLC image
dmlc_path = 'perfect_dmlc_drmlc.dcm'
as1200 = AS1200Image()
for offset in (-40, -10, 20, 50):
as1200.add_layer(PerfectFieldLayer((150, 20), cax_offset_mm=(0, offset)))
for offset in (-45, -15, 15, 45):
as1200.add_layer(PerfectFieldLayer((150, 19.5), cax_offset_mm=(0, offset)))
as1200.add_layer(GaussianFilterLayer(sigma_mm=2))
as1200.generate_dicom(file_out_name=dmlc_path)

Expand Down Expand Up @@ -289,16 +289,16 @@ We now add a horn effect and random noise to the data:
# open image
open_path = 'noisy_open_drmlc.dcm'
as1200 = AS1200Image()
as1200.add_layer(FilteredFieldLayer(field_size_mm=(150, 110), cax_offset_mm=(0, 5)))
as1200.add_layer(FilteredFieldLayer(field_size_mm=(150, 85), cax_offset_mm=(0, 0)))
as1200.add_layer(GaussianFilterLayer(sigma_mm=2))
as1200.add_layer(RandomNoiseLayer(sigma=0.03))
as1200.generate_dicom(file_out_name=open_path)

# DMLC image
dmlc_path = 'noisy_dmlc_drmlc.dcm'
as1200 = AS1200Image()
for offset in (-40, -10, 20, 50):
as1200.add_layer(FilteredFieldLayer((150, 20), cax_offset_mm=(0, offset)))
for offset in (-45, -15, 15, 45):
as1200.add_layer(FilteredFieldLayer((150, 19.5), cax_offset_mm=(0, offset)))
as1200.add_layer(GaussianFilterLayer(sigma_mm=2))
as1200.add_layer(RandomNoiseLayer(sigma=0.03))
as1200.generate_dicom(file_out_name=dmlc_path)
Expand Down Expand Up @@ -336,18 +336,18 @@ Let's now get devious and randomly adjust the height of each ROI (effectively ch
# open image
open_path = 'noisy_open_drmlc.dcm'
as1200 = AS1200Image()
as1200.add_layer(FilteredFieldLayer(field_size_mm=(150, 110), cax_offset_mm=(0, 5)))
as1200.add_layer(FilteredFieldLayer(field_size_mm=(150, 85), cax_offset_mm=(0, 0)))
as1200.add_layer(GaussianFilterLayer(sigma_mm=2))
as1200.add_layer(RandomNoiseLayer(sigma=0.03))
as1200.generate_dicom(file_out_name=open_path)

# DMLC image
dmlc_path = 'noisy_dmlc_drmlc.dcm'
as1200 = AS1200Image()
for offset in (-40, -10, 20, 50):
as1200.add_layer(FilteredFieldLayer((150, 20), cax_offset_mm=(0, offset), alpha=random.uniform(0.93, 1)))
for offset in (-45, -15, 15, 45):
as1200.add_layer(FilteredFieldLayer((150, 19.5), cax_offset_mm=(0, offset), alpha=random.uniform(0.93, 1)))
as1200.add_layer(GaussianFilterLayer(sigma_mm=2))
as1200.add_layer(RandomNoiseLayer(sigma=0.03))
as1200.add_layer(RandomNoiseLayer(sigma=0.04))
as1200.generate_dicom(file_out_name=dmlc_path)

# analyze it
Expand Down
6 changes: 3 additions & 3 deletions docs/source/winston_lutz.rst
Original file line number Diff line number Diff line change
Expand Up @@ -890,8 +890,8 @@ values. We offset the BB to the left by 2mm for visualization purposes:
AS1200Image(1000),
FilteredFieldLayer,
dir_out=wl_dir,
final_layers=[GaussianFilterLayer(), ],
bb_size_mm=4,
final_layers=[GaussianFilterLayer(sigma_mm=1), ],
bb_size_mm=5,
field_size_mm=(20, 20),
offset_mm_left=2,
image_axes=[(0, 0, 0), (0, 45, 0), (0, 270, 0),
Expand All @@ -900,7 +900,7 @@ values. We offset the BB to the left by 2mm for visualization purposes:
)

wl = pylinac.WinstonLutz(wl_dir)
wl.analyze(bb_size_mm=4)
wl.analyze(bb_size_mm=5)
print(wl.results())
wl.plot_images(axis=Axis.GBP_COMBO)

Expand Down
2 changes: 0 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ def build_docs(session):
"docs/build",
"-W",
"--keep-going",
"-j",
"auto",
"-a",
"-E",
"-b",
Expand Down
16 changes: 8 additions & 8 deletions pylinac/core/image_generator/layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def __init__(
----------
field_size_mm
Field size in mm at the iso plane as (width, height)
Field size in mm at the iso plane as (height, width)
cax_offset_mm
The offset in mm. (down, right)
alpha
Expand Down Expand Up @@ -239,7 +239,7 @@ def __init__(
----------
field_size_mm
Field size in mm at the iso plane
Field size in mm at the iso plane (height, width)
cax_offset_mm
The offset in mm. (out, right)
alpha
Expand Down Expand Up @@ -299,7 +299,7 @@ def __init__(
----------
field_size_mm
Field size in mm at the iso plane
Field size in mm at the iso plane (height, width).
cax_offset_mm
The offset in mm. (out, right)
alpha
Expand Down Expand Up @@ -442,7 +442,7 @@ def draw_rotated_rectangle(
center : list
The center of the rectangle.
extent : list
The width and height of the rectangle.
The height and width of the rectangle.
angle : float
The angle of rotation in degrees.
Expand All @@ -457,10 +457,10 @@ def draw_rotated_rectangle(
"""
# Calculate rectangle coordinates before rotation
# follows row,col convention of numpy; y = row, x = col
x0 = center[1] - extent[0] / 2
x1 = center[1] + extent[0] / 2
y0 = center[0] - extent[1] / 2
y1 = center[0] + extent[1] / 2
x0 = center[1] - extent[1] / 2
x1 = center[1] + extent[1] / 2
y0 = center[0] - extent[0] / 2
y1 = center[0] + extent[0] / 2

rect_coords = np.array([[x0, y0], [x1, y0], [x1, y1], [x0, y1]])

Expand Down
4 changes: 2 additions & 2 deletions pylinac/core/image_generator/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ def generate_picketfence(
pos += picket_offset_error[idx]
if orientation == orientation.UP_DOWN:
position = (0, pos)
layout = (picket_width_mm, picket_height_mm)
layout = (picket_height_mm, picket_width_mm)
else:
position = (pos, 0)
layout = (picket_height_mm, picket_width_mm)
layout = (picket_width_mm, picket_height_mm)
simulator.add_layer(field_layer(field_size_mm=layout, cax_offset_mm=position))
if final_layers is not None:
for layer in final_layers:
Expand Down
Loading

0 comments on commit 8eb19fa

Please sign in to comment.