Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions examples/PlanOptimization/run_4DProtonOptimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
from opentps.core.data.plan._protonPlanDesign import ProtonPlanDesign
from opentps.core.data import DVH
from opentps.core.data import Patient
from opentps.core.data.plan import FidObjective
from opentps.core.io import mcsquareIO
from opentps.core.io.dataLoader import readData
from opentps.core.io.scannerReader import readScanner
Expand All @@ -47,6 +46,7 @@
from opentps.core.processing.planOptimization.planOptimization import IntensityModulationOptimizer
from opentps.core.processing.imageProcessing.resampler3D import resampleImage3DOnImage3D
from opentps.core.io.dicomIO import writeRTDose, readDicomDose
import opentps.core.processing.planOptimization.objectives.dosimetricObjectives as doseObj


logger = logging.getLogger(__name__)
Expand All @@ -68,8 +68,8 @@
#%%
#CT and ROI creation
#---------------------------
# Generic example: 4DCT composed of 3 CTs : 2 phases and the MidP.
# The anatomy consists of a square target moving vertically, with an organ at risk and soft tissue (muscle) in front of it.
# Generic example: 4DCT composed of 3 CTs : 2 phases and the MidP.
# The anatomy consists of a square target moving vertically, with an organ at risk and soft tissue (muscle) in front of it.
CT4D = []
ROI4D = []
for i in range(0, 3):
Expand All @@ -87,7 +87,7 @@
Patient.id = f'12082024'
Patient.birthDate = dt.strftime('%Y%m%d')
patient.sex = ""

ctSize = 150
ct = CTImage(seriesInstanceUID=ctSeriesInstanceUID, frameOfReferenceUID=frameOfReferenceUID)
ct.name = f'CT_Phase_{i}'
Expand Down Expand Up @@ -130,7 +130,7 @@
data[25:45, 50:80, 65:85] = True
TV.imageArray = data
ROI.append(TV)

# Muscle
Muscle = ROIMask()
Muscle.patient = patient
Expand Down Expand Up @@ -225,16 +225,16 @@
# planDesign.robustness.RandomPeriodError = 5.0 # %
# planDesign.robustness.Breathing_period = 1 # x100% # default value

planDesign.spotSpacing = 10.0
planDesign.layerSpacing = 10.0
planDesign.spotSpacing = 10.0
planDesign.layerSpacing = 10.0
planDesign.targetMargin = 7 # Enough to encompass target motion

planDesign.defineTargetMaskAndPrescription(target = RefTV, targetPrescription = 60.)

plan = planDesign.buildPlan()
plan.rtPlanName = f"RobustPlan_4D"

# refIndex :
# refIndex :
# ACCUMULATED -> Index of the Image in the 4DCT one wish we will accumulate the dose.
## SYSTEMATIC -> Index of the Image in the 4DCT who will be used as the nominal. So the one closer to the MidP. Or the Midp.

Expand All @@ -248,10 +248,10 @@
#%%
# Set objectives
#----------------------
plan.planDesign.objectives.addFidObjective(RefTV, FidObjective.Metrics.DMAX, limitValue = 63.0, weight = 100.0, robust=True)
plan.planDesign.objectives.addFidObjective(RefTV, FidObjective.Metrics.DMIN, limitValue = 60.0, weight = 100.0, robust=True)
plan.planDesign.objectives.addFidObjective(RefOAR, FidObjective.Metrics.DMAX, limitValue = 40.0, weight = 80.0)
plan.planDesign.objectives.addFidObjective(RefBody, FidObjective.Metrics.DMAX, limitValue = 40.0, weight = 80.0)
plan.planDesign.objectives.addObjective(doseObj.DMax(RefTV,63, weight=100.0, robust=True))
plan.planDesign.objectives.addObjective(doseObj.DMin(RefTV,60, weight=100.0, robust=True))
plan.planDesign.objectives.addObjective(doseObj.DMax(RefOAR, 40, weight=80.0))
plan.planDesign.objectives.addObjective(doseObj.DMax(RefBody, 40, weight=80.0))

#%%
# Optimize treatment plan
Expand Down
52 changes: 40 additions & 12 deletions examples/PlanOptimization/run_SimpleOptimizationPhoton.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@
from opentps.core.data.images import ROIMask
from opentps.core.data import DVH
from opentps.core.data import Patient
from opentps.core.data.plan import FidObjective
from opentps.core.io.scannerReader import readScanner
from opentps.core.io.serializedObjectIO import saveRTPlan, loadRTPlan
from opentps.core.processing.doseCalculation.doseCalculationConfig import DoseCalculationConfig
from opentps.core.processing.imageProcessing.resampler3D import resampleImage3DOnImage3D
from opentps.core.processing.planOptimization.planOptimization import IntensityModulationOptimizer
from opentps.core.processing.doseCalculation.photons.cccDoseCalculator import CCCDoseCalculator
from opentps.core.data.plan import PhotonPlanDesign
import opentps.core.processing.planOptimization.objectives.dosimetricObjectives as doseObj

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -88,6 +88,12 @@
data[100:120, 100:120, 100:120] = True
roi.imageArray = data

body = roi.copy()
body.name = 'Body'
body.dilateMask(20)
body.imageArray = np.logical_xor(body.imageArray, roi.imageArray).astype(bool)


#%%
#Output path
#-----------
Expand Down Expand Up @@ -127,21 +133,38 @@
planDesign.xBeamletSpacing_mm = 5
planDesign.yBeamletSpacing_mm = 5
planDesign.targetMargin = 5.0
planDesign.defineTargetMaskAndPrescription(target = roi, targetPrescription = 20.)

plan = planDesign.buildPlan()
planDesign.defineTargetMaskAndPrescription(target = roi, targetPrescription = 20.)

beamlets = ccc.computeBeamlets(ct, plan)
plan = planDesign.buildPlan()

beamlets = ccc.computeBeamlets(ct, plan)
doseInfluenceMatrix = copy.deepcopy(beamlets)

plan.planDesign.beamlets = beamlets
beamlets.storeOnFS(os.path.join(output_path, "BeamletMatrix_" + plan.seriesInstanceUID + ".blm"))
# Save plan with initial spot weights in serialized format (OpenTPS format)
saveRTPlan(plan, plan_file)

plan.planDesign.objectives.addObjective(doseObj.DMax(body,5, weight=1.0))

plan.planDesign.objectives.addObjective(doseObj.DMax(roi, 21, weight=10.0))
plan.planDesign.objectives.addObjective(doseObj.DMin(roi, 20, weight=20.0))

# Other examples of objectives

# plan.planDesign.objectives.addObjective(doseObj.DUniform(roi, 20, weight=10))
#
# plan.planDesign.objectives.addObjective(doseObj.DMaxMean(roi,21,weight=5))
# plan.planDesign.objectives.addObjective(doseObj.DMinMean(roi,20,weight=5))
# plan.planDesign.objectives.addObjective(doseObj.DFallOff(roi,oar,10,5,15,weight=5))
#
# plan.planDesign.objectives.addObjective(doseObj.DVHMax(roi, 21,0.05, weight=5))
# plan.planDesign.objectives.addObjective(doseObj.DVHMin(roi, 18,0.90, weight=5))
#
# plan.planDesign.objectives.addObjective(doseObj.EUDMin(roi, 19, 1, weight=50))
# plan.planDesign.objectives.addObjective(doseObj.EUDMax(roi, 21, 1, weight=50))
# plan.planDesign.objectives.addObjective(doseObj.EUDUniform(roi, 20, 1, weight=50))

plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DMAX, 20.0, 1.0)
plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DMIN, 20.5, 1.0)

plan.numberOfFractionsPlanned = 30

Expand All @@ -166,6 +189,7 @@
#---------------------------------

target_DVH = DVH(roi, doseImage)
body_DVH = DVH(body, doseImage)
print('D5 - D95 = {} Gy'.format(target_DVH.D5 - target_DVH.D95))
clinROI = [roi.name, roi.name]
clinMetric = ["Dmin", "Dmax"]
Expand All @@ -178,13 +202,14 @@
# center of mass
#---------------
roi = resampleImage3DOnImage3D(roi, ct)
body = resampleImage3DOnImage3D(body,ct)
COM_coord = roi.centerOfMass
COM_index = roi.getVoxelIndexFromPosition(COM_coord)
Z_coord = COM_index[2]

img_ct = ct.imageArray[:, :, Z_coord].transpose(1, 0)
contourTargetMask = roi.getBinaryContourMask()
img_mask = contourTargetMask.imageArray[:, :, Z_coord].transpose(1, 0)
img_mask = roi.imageArray[:, :, Z_coord].transpose(1, 0)
img_body = body.imageArray[:, :, Z_coord].transpose(1, 0)
img_dose = resampleImage3DOnImage3D(doseImage, ct)
img_dose = img_dose.imageArray[:, :, Z_coord].transpose(1, 0)

Expand All @@ -195,10 +220,13 @@
ax[0].axes.get_xaxis().set_visible(False)
ax[0].axes.get_yaxis().set_visible(False)
ax[0].imshow(img_ct, cmap='gray')
ax[0].imshow(img_mask, alpha=.2, cmap='binary') # PTV
ax[0].contour(img_body,[0.5],colors='green') # body
ax[0].contour(img_mask,[0.5],colors='red') # PTV

dose = ax[0].imshow(img_dose, cmap='jet', alpha=.2)
plt.colorbar(dose, ax=ax[0])
ax[1].plot(target_DVH.histogram[0], target_DVH.histogram[1], label=target_DVH.name)
ax[1].plot(target_DVH.histogram[0], target_DVH.histogram[1], label=target_DVH.name,color='red')
ax[1].plot(body_DVH.histogram[0], body_DVH.histogram[1], label=body_DVH.name,color='green')
ax[1].set_xlabel("Dose (Gy)")
ax[1].set_ylabel("Volume (%)")
ax[1].grid(True)
Expand Down
77 changes: 51 additions & 26 deletions examples/PlanOptimization/run_SimpleOptimizationProton.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@

#%%
#import the needed opentps.core packages

import opentps.core.processing.planOptimization.objectives.dosimetricObjectives as doseObj
from opentps.core.data.images import CTImage
from opentps.core.data.images import ROIMask
from opentps.core.data.plan import ObjectivesList
from opentps.core.data.plan import ProtonPlanDesign
from opentps.core.data import DVH
from opentps.core.data import Patient
from opentps.core.data.plan import FidObjective
from opentps.core.io import mcsquareIO
from opentps.core.io.scannerReader import readScanner
from opentps.core.io.serializedObjectIO import saveRTPlan, loadRTPlan
Expand Down Expand Up @@ -81,6 +80,20 @@
data[100:120, 100:120, 100:120] = True
roi.imageArray = data

# roi2 = ROIMask()
# roi2.patient = patient
# roi2.name = 'TV2'
# roi2.color = (0, 0, 255) # blue
# data = np.zeros((ctSize, ctSize, ctSize)).astype(bool)
# data[100:120, 70:100, 70:120] = True
# roi2.imageArray = data


body = roi.copy()
body.name = 'Body'
body.dilateMask(20)
body.imageArray = np.logical_xor(body.imageArray, roi.imageArray).astype(bool)

#%%
#Configure dose engine
#---------------------
Expand All @@ -91,10 +104,10 @@
mc2.ctCalibration = ctCalibration

mc2._independentScoringGrid = True
scoringSpacing = [2, 2, 2]
scoringSpacing = [2,2,2]
mc2._scoringVoxelSpacing = scoringSpacing

#%%
#%%#
#Design plan
#-----------

Expand All @@ -108,48 +121,55 @@
planInit.beamNames = beamNames
planInit.couchAngles = couchAngles
planInit.calibration = ctCalibration
planInit.spotSpacing = 6.0
planInit.layerSpacing = 6.0
planInit.targetMargin = 0.0
planInit.spotSpacing = 5.0
planInit.layerSpacing = 5.0
planInit.targetMargin = 2.0
planInit.setScoringParameters(scoringSpacing=[2, 2, 2], adapt_gridSize_to_new_spacing=True)
# needs to be called after scoringGrid settings but prior to spot placement
planInit.defineTargetMaskAndPrescription(target = roi, targetPrescription = 20.)
planInit.defineTargetMaskAndPrescription(target = roi, targetPrescription = 20.)

plan = planInit.buildPlan() # Spot placement
plan.PlanName = "NewPlan"

beamlets = mc2.computeBeamlets(ct, plan, roi=[roi])
beamlets = mc2.computeBeamlets(ct, plan, roi=[roi,body])
plan.planDesign.beamlets = beamlets
# doseImageRef = beamlets.toDoseImage()

#%%
#objectives
#----------
plan.planDesign.objectives.addObjective(doseObj.DMax(body,5, weight=1.0))

plan.planDesign.objectives.addObjective(doseObj.DMax(roi, 21, weight=10.0))
plan.planDesign.objectives.addObjective(doseObj.DMin(roi, 20, weight=20.0))

plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DMAX, 20.0, 1.0)
plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DMIN, 20.5, 1.0)
# Other examples of objectives
# plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DMEAN, 20, 1.0)
# plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DUNIFORM, 20, 1.0)
# plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DVHMIN, 19, 1.0, volume = 95)
# plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DVHMAX, 21, 1.0, volume = 5)
# plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.EUDMIN, 19.5, 1.0, EUDa = 0.2)
# plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.EUDMAX, 20, 1.0, EUDa = 1)
# plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.EUDUNIFORM, 20.5, 1.0, EUDa = 0.5)
# plan.planDesign.objectives.addFidObjective(BODY, FidObjective.Metrics.DFALLOFF, weight=10, fallOffDistance=1, fallOffLowDoseLevel=0, fallOffHighDoseLevel=21)

# plan.planDesign.objectives.addObjective(doseObj.DUniform(roi, 20, weight=10))
#
# plan.planDesign.objectives.addObjective(doseObj.DMaxMean(roi,21,weight=5))
# plan.planDesign.objectives.addObjective(doseObj.DMinMean(roi,20,weight=5))
# plan.planDesign.objectives.addObjective(doseObj.DFallOff(roi,oar,10,5,15,weight=5))
#
# plan.planDesign.objectives.addObjective(doseObj.DVHMax(roi, 21,0.05, weight=5))
# plan.planDesign.objectives.addObjective(doseObj.DVHMin(roi, 18,0.90, weight=5))
#
# plan.planDesign.objectives.addObjective(doseObj.EUDMin(roi, 19, 1, weight=50))
# plan.planDesign.objectives.addObjective(doseObj.EUDMax(roi, 21, 1, weight=50))
# plan.planDesign.objectives.addObjective(doseObj.EUDUniform(roi, 20, 1, weight=50))

#%%
#Optimize plan
#-------------

solver = IntensityModulationOptimizer(method='Scipy_L-BFGS-B', plan=plan, maxiter=50)
solver = IntensityModulationOptimizer(method='Scipy_L-BFGS-B', plan=plan, maxiter=50, hardwareAcceleration = None)
doseImage, ps = solver.optimize()

#%%
#Final dose computation
#----------------------

mc2.nbPrimaries = 1e7
mc2.nbPrimaries = 1e6
doseImage = mc2.computeDose(ct, plan)

#%%
Expand All @@ -162,16 +182,19 @@
print('D95 = ' + str(target_DVH.D95) + ' Gy')
print('D5 = ' + str(target_DVH.D5) + ' Gy')
print('D5 - D95 = {} Gy'.format(target_DVH.D5 - target_DVH.D95))
bodyResampled = resampleImage3D(body, origin=ct.origin, spacing=scoringSpacing)
body_DVH = DVH(bodyResampled, doseImage)

# center of mass
roi = resampleImage3DOnImage3D(roi, ct)
body = resampleImage3DOnImage3D(body,ct)
COM_coord = roi.centerOfMass
COM_index = roi.getVoxelIndexFromPosition(COM_coord)
Z_coord = COM_index[2]

img_ct = ct.imageArray[:, :, Z_coord].transpose(1, 0)
contourTargetMask = roi.getBinaryContourMask()
img_mask = contourTargetMask.imageArray[:, :, Z_coord].transpose(1, 0)
img_mask = roi.imageArray[:, :, Z_coord].transpose(1, 0)
img_body = body.imageArray[:, :, Z_coord].transpose(1, 0)
img_dose = resampleImage3DOnImage3D(doseImage, ct)
img_dose = img_dose.imageArray[:, :, Z_coord].transpose(1, 0)

Expand All @@ -183,10 +206,12 @@
# Display dose
fig, ax = plt.subplots(1, 2, figsize=(12, 5))
ax[0].imshow(img_ct, cmap='gray')
ax[0].imshow(img_mask, alpha=.2, cmap='binary') # PTV
ax[0].contour(img_body,[0.5],colors='green') # Body
ax[0].contour(img_mask,[0.5],colors='red') # PTV
dose = ax[0].imshow(img_dose, cmap='jet', alpha=.2)
plt.colorbar(dose, ax=ax[0])
ax[1].plot(target_DVH.histogram[0], target_DVH.histogram[1], label=target_DVH.name)
ax[1].plot(target_DVH.histogram[0], target_DVH.histogram[1], label=target_DVH.name,color='red')
ax[1].plot(body_DVH.histogram[0], body_DVH.histogram[1], label=body_DVH.name,color='green')
ax[1].set_xlabel("Dose (Gy)")
ax[1].set_ylabel("Volume (%)")
plt.grid(True)
Expand Down
Loading