Skip to content

Commit

Permalink
ENH: Dynamic arc beam sequence GUI and logic upgrades
Browse files Browse the repository at this point in the history
1. Logic debug information was removed
2. ExternalBeamPlanning module description file was created
  • Loading branch information
Mikhail Polkovnikov committed Aug 10, 2022
1 parent 0294eae commit 0a3415c
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 142 deletions.
64 changes: 3 additions & 61 deletions Beams/Logic/vtkSlicerBeamsModuleLogic.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,6 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
{
angles.push_back(finalAngle);
}
std::cout << "Angles " << angles.size() << '\n';
std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; });
std::cout << std::endl;
}
else if (!direction && initialAngle > finalAngle) // CW, ini > fin
{
Expand All @@ -368,9 +365,6 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
{
angles.push_back(finalAngle);
}
std::cout << "Angles " << angles.size() << '\n';
std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; });
std::cout << std::endl;
}
else if (direction && initialAngle < finalAngle) // CCW, ini < fin
{
Expand All @@ -395,9 +389,6 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
{
angles.push_back(finalAngle);
}
std::cout << "Angles " << angles.size() << '\n';
std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; });
std::cout << std::endl;
}
else if (direction && initialAngle > finalAngle) // CCW, ini > fin
{
Expand All @@ -409,53 +400,7 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
{
angles.push_back(finalAngle);
}
std::cout << "Angles " << angles.size() << '\n';
std::for_each( angles.begin(), angles.end(), [](double v){ std::cout << v << ' '; });
std::cout << std::endl;
}
/*
if (finalAngle < 0. && finalAngle > 360. && initialAngle < 0. && initialAngle > 360.)
{
return false;
}
if (!direction && initialAngle < finalAngle) // CW, ini < fin
{
for (double angle = initialAngle; angle <= finalAngle; angle += 1.)
{
angles.push_back(angle);
}
}
else if (!direction && initialAngle > finalAngle) // CW, ini > fin
{
for (double angle = initialAngle; angle <= 360.; angle += 1.)
{
angles.push_back(angle);
}
for (double angle = 1.; angle <= finalAngle; angle += 1.)
{
angles.push_back(angle);
}
}
else if (direction && initialAngle < finalAngle) // CCW, ini < fin
{
for (double angle = initialAngle; angle >= 0.0; angle -= 1.)
{
angles.push_back(angle);
}
for (double angle = 359.; angle >= finalAngle; angle -= 1.)
{
angles.push_back(angle);
}
}
else if (direction && initialAngle > finalAngle) // CCW, ini > fin
{
for (double angle = initialAngle; angle >= finalAngle; angle -= 1.)
{
angles.push_back(angle);
}
}
*/
if (angles.size() < 2)
{
vtkErrorMacro("CreateArcBeamDynamicSequence: Number of angle elements is less than 2");
Expand All @@ -464,7 +409,7 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(

vtkMRMLScene* scene = planNode->GetScene();

vtkMRMLSubjectHierarchyNode* shNode = vtkMRMLSubjectHierarchyNode::GetSubjectHierarchyNode(scene);
vtkMRMLSubjectHierarchyNode* shNode = scene->GetSubjectHierarchyNode();
if (!shNode)
{
vtkErrorMacro("CreateArcBeamDynamicSequence: Failed to access subject hierarchy node");
Expand Down Expand Up @@ -521,10 +466,7 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
// SAD for RTPlan, source to beam limiting devices (Jaws, MLC)
if (beamNode)
{
// beamNode->SetSAD(rtReader->GetBeamSourceAxisDistance(dicomBeamNumber));
// beamNode->SetSourceToJawsDistanceX(rtReader->GetBeamSourceToJawsDistanceX(dicomBeamNumber));
// beamNode->SetSourceToJawsDistanceY(rtReader->GetBeamSourceToJawsDistanceY(dicomBeamNumber));
// beamNode->SetSourceToMultiLeafCollimatorDistance(rtReader->GetBeamSourceToMultiLeafCollimatorDistance(dicomBeamNumber));
// TODO: Add beam parameters for each angle step
}

// Add beam to beam sequence node
Expand All @@ -545,7 +487,7 @@ bool vtkSlicerBeamsModuleLogic::CreateArcBeamDynamicSequence(
this->UpdateTransformForBeam( beamSequenceNode->GetSequenceScene(), beamNode, transformNode, isocenter);

vtkTransform* transform = vtkTransform::SafeDownCast(transformNode->GetTransformToParent());
if (isocenter)
if (transform)
{
// Actual translation to isocenter
transform->Translate( isocenter[0], isocenter[1], isocenter[2]);
Expand Down
30 changes: 15 additions & 15 deletions Beams/Logic/vtkSlicerBeamsModuleLogic.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,24 @@ class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerBeamsModuleLogic :

/// Update parent transform of a given beam using its parameters and the IEC logic
/// without using plan node (only isocenter position)
/// @param beamSequenceScene - inner scene of the beam sequence node
/// @param beamNode - a current beam node (must be added to the beam sequence node beforehand)
/// @param beamTransformNode - parent transform of the beam according to the beam parameters and isocenter
/// @param isocenter - isocenter position
/// \param beamSequenceScene inner scene of the beam sequence node
/// \param beamNode a current beam node (must be added to the beam sequence node beforehand)
/// \param beamTransformNode parent transform of the beam according to the beam parameters and isocenter
/// \param isocenter isocenter position
/// \warning This method is used only in vtkSlicerDicomRtImportExportModuleLogic::vtkInternal::LoadDynamicBeamSequence
void UpdateTransformForBeam( vtkMRMLScene* beamSequenceScene, vtkMRMLRTBeamNode* beamNode,
void UpdateTransformForBeam(vtkMRMLScene* beamSequenceScene, vtkMRMLRTBeamNode* beamNode,
vtkMRMLLinearTransformNode* beamTransformNode, double isocenter[3]);

/// Create arc delivery beam sequence
/// @param initialAngle - initial angle in degrees
/// @param finalAngle - final angle in degrees
/// @param angleStep - single angle step within arc
/// @param direction - 0 - clockwise, 1 - counter-clockwise
/// @param planNode - input plan node, which contains reference volume node and isocenter position
/// @param sequenceBrowserNode - output sequence browser node
/// @param sequenceBeamNode - output beam node
/// @param sequenceTransformNode - output transform node
bool CreateArcBeamDynamicSequence( double initialAngle, double finalAngle,
/// \param initialAngle initial angle in degrees
/// \param finalAngle final angle in degrees
/// \param angleStep single angle step within arc
/// \param direction 0 - clockwise, 1 - counter-clockwise
/// \param planNode input plan node, which contains reference volume node and isocenter position
/// \param sequenceBrowserNode output sequence browser node
/// \param sequenceBeamNode output beam node
/// \param sequenceTransformNode output transform node
bool CreateArcBeamDynamicSequence(double initialAngle, double finalAngle,
double angleStep, bool direction, vtkMRMLRTPlanNode* planNode,
vtkMRMLSequenceBrowserNode* beamSequenceBrowserNode,
vtkMRMLSequenceNode* beamSequenceNode, vtkMRMLSequenceNode* transformSequenceNode);
Expand All @@ -95,7 +95,7 @@ class VTK_SLICER_BEAMS_LOGIC_EXPORT vtkSlicerBeamsModuleLogic :
vtkSlicerBeamsModuleLogic(const vtkSlicerBeamsModuleLogic&) = delete;
void operator=(const vtkSlicerBeamsModuleLogic&) = delete;

vtkSlicerMLCPositionLogic* MLCPositionLogic;
vtkSlicerMLCPositionLogic* MLCPositionLogic{ nullptr };
};

#endif
Expand Down
5 changes: 2 additions & 3 deletions DicomRtImportExport/Logic/vtkSlicerDicomRtReader.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ class vtkSlicerDicomRtReader::vtkInternal
std::string InstitutionalDepartmentName;
std::string ManufacturerModelName;
};

/// List of loaded beams from external beam plan
std::vector<BeamEntry> BeamSequenceVector;

Expand Down Expand Up @@ -589,9 +589,8 @@ bool vtkSlicerDicomRtReader::vtkInternal::BeamEntry::IsArcDeliverySequence(
(cp1.GantryAngle >= 0. && cp1.GantryAngle <= 360.);

res = point0 && point1;
if (point0 && point1)
if (res)
{
res = true;
initialAngle = cp0.GantryAngle;
finalAngle = cp1.GantryAngle;
// rotation: 0 - clockwise, 1 - counter clockwise
Expand Down
1 change: 1 addition & 0 deletions DicomRtImportExport/Logic/vtkSlicerDicomRtReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

// STD includes
#include <vector>
#include <array>

class vtkPolyData;

Expand Down
101 changes: 101 additions & 0 deletions Docs/user_guide/modules/externalbeamplanning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
## "External Beam Planning" module description

The ExternalBeamPlanning module is a generic, extensible module for forward
planning of external beam radiation therapy treatments.

The "External Beam Planning" module is responsible for creating a beam plan or ion beam plan
according to DICOM RT standard. Only a few basic features are supported.
Each plan consists of a number of beams: static or dynamic. Each plan supports
only one isocenter point, multiple isocenters per a single plan are not allowed.

This work was in part funded by An Applied Cancer Research Unit of Cancer Care Ontario
with funds provided by the Ministry of Health and Long-Term Care and the Ontario Consortium
for Adaptive Interventions in Radiation Oncology (OCAIRO) to provide free, open-source toolset
for radiotherapy and related image-guided interventions.

Author: Csaba Pinter (PerkLab, Queen's University), Greg Sharp (Massachusetts General Hospital)
Contact: Csaba Pinter, <email>[email protected]</email>

![image](https://www.slicer.org/w/img_auth.php/3/3f/LogoCco.png)
![image](https://www.slicer.org/w/img_auth.php/2/27/LogoOCAIRO.jpg)

### Use Cases
- Proton dose calculation
- Any dose engine can be integrated (C++, Python, Matlab)

### Tutorials
[Orthovoltage RT treatment planning tutorial](https://github.com/SlicerRt/SlicerRtDoc/blob/master/tutorials/SlicerRT_Tutorial_OrthovoltageDoseEngine.pptx) (uses EGSnrc)

### Graphical User Interface loadable module (GUI). Panels and their use

![image](https://user-images.githubusercontent.com/3785912/183899734-d4795313-fd39-4c40-8df6-e84f0469c3b0.png)

Loadable GUI module "External Beam Planning" is used to setup beam parameters
for beams or ion beams, or modify already created beams.
Static RT beam is setup by: Isocenter position, SAD, SID, Jaws borders, MLC positions. Static beam is fixed.

Dynamic RT and Ion RT beams are setup by: initial and final angles of rotation around gantry axis,
direction of rotation, isocenter position, SAD, SID, Jaws borders, MLC positions, scanning spot map, etc.
Dynamic beam changes it position over time by means of rotation or (and) changing the position of phase space of the beam.

#### Active RT plan

![image](https://user-images.githubusercontent.com/3785912/183899795-762b8b13-4ad5-4112-bcb6-e3f5fbd21fe2.png)

1. **Active RT plan**: Selected RT Plan
2. **Ion plan**: flag to generate RTIonPlan instead of RTPlan

#### Plan parameters

![image](https://user-images.githubusercontent.com/3785912/183899832-fb178c71-900f-48d8-97a5-35c4d532bfb3.png)

1. **Reference volume**: Reference volume node data (usually CT)
2. **Structure set**: Segmentation node data
3. **Points of interest**: Markups fiducial to be used as isocenter
4. **Isocenter**: Isocenter position in RAS
5. **Center views**: Center slice views on isocenter
6. **Target volume**: Select targer ROI
7. **Isocenter at target center**: Set isocenter in the center of the targer ROI
8. **Dose engine**: Select a dose engine calculator **Not implemented**
9. **Rx Dose (Gy)**: Setup an amount of dose in Gy

#### Output

![image](https://user-images.githubusercontent.com/3785912/183899869-580d00a9-aa86-405d-b22c-d11a35f2f6df.png)

1. **Output dose volume**: Setup output dose volume
2. **Clear dose**: Clear dose volume
3. **Calculate WED**: Calculate water-equivalent distance **Not implemented**
4. **Calculate Dose**: Calculate dose according to the engine parameters **Not implemented**

#### Beams

![image](https://user-images.githubusercontent.com/3785912/183899904-6f8e76fd-1574-462e-bf31-21f2cf5fa740.png)

1. **Arc beam sequence**: Activate dynamic beam arc movement around gantry rotation axis
2. **Initial angle**: Initial angle position in degrees
3. **Final angle**: Final angle position in degrees
4. **Angle step**: Angle step
5. **Rotation direction**: Clockwise (CW) of CounterClockwise (CCW) rotation
5. **Add beam**: Add static or dynamic arc beam to the plan
6. **Remove beam**: Remove selected beam from the plan

### Arc beam

The beam movement around rotation axis could be used to calculate a filtered back-projection reconstruction for cone-beam geometries.
For fast forward and back projection reconstruction the [RTK](https://www.openrtk.org) library can be used.

### References

1. Sharp, G., Pinter, C., Unkelbach, J., Fichtinger, G. (2017). Open Source Proton Treatment Planning in 3D Slicer: Status Update. Proceedings to the 56 Annual Meeting of the Particle Therapy Cooperative Group (PTCOG), 8-13 May 2017. International Journal of Particle Therapy: Summer 2017, Vol. 4, No. 1, pp. 14-83.

### Information for Developers

- Sample C++ dose engine: [https://github.com/SlicerRt/SlicerRT/blob/master/ExternalBeamPlanning/Widgets/qSlicerMockDoseEngine.h](https://github.com/SlicerRt/SlicerRT/blob/master/ExternalBeamPlanning/Widgets/qSlicerMockDoseEngine.h)
- Sample Python dose engine: [https://github.com/SlicerRt/SlicerRT/blob/master/ExternalBeamPlanning/Widgets/Python/MockPythonDoseEngine.py](https://github.com/SlicerRt/SlicerRT/blob/master/ExternalBeamPlanning/Widgets/Python/MockPythonDoseEngine.py)
- Future plans
- Inverse planning capabilities
- Matlab plugin adapter
- Ports module (apertures, MLC, target volume)
- Beam groups (common parameters for a group of beams)

Loading

0 comments on commit 0a3415c

Please sign in to comment.