From 41bd6e5254ebd99f982d6cd43f4bfd93a798b5cb Mon Sep 17 00:00:00 2001 From: Csaba Pinter Date: Mon, 21 Nov 2022 12:26:16 +0000 Subject: [PATCH] ENH: Add real-time mode to Isodose computation Real-time mode can be enabled using the RealTime flag in the Isodose parameter node. When this flag is enabled, the following functions are prevented: use of subject hierarchy to organize the isodose model nodes, reporting of progress, batch processing, and update of dose color table from the isodose one. Instead, top-level isodose model nodes are re-used (by node name) at every computation. It is useful when isodose is needed to be computed on-the-fly for streamed dose data, when one set of isodose surfaces (the "current one") is all that is needed to be kept and displayed. --- Isodose/Logic/vtkSlicerIsodoseModuleLogic.cxx | 69 ++++++++++++++----- Isodose/MRML/vtkMRMLIsodoseNode.cxx | 17 ++--- Isodose/MRML/vtkMRMLIsodoseNode.h | 51 ++++++++++---- 3 files changed, 95 insertions(+), 42 deletions(-) diff --git a/Isodose/Logic/vtkSlicerIsodoseModuleLogic.cxx b/Isodose/Logic/vtkSlicerIsodoseModuleLogic.cxx index 743f5504..ccccd4a4 100644 --- a/Isodose/Logic/vtkSlicerIsodoseModuleLogic.cxx +++ b/Isodose/Logic/vtkSlicerIsodoseModuleLogic.cxx @@ -613,14 +613,21 @@ bool vtkSlicerIsodoseModuleLogic::CreateIsodoseSurfaces(vtkMRMLIsodoseNode* para return false; } - scene->StartState(vtkMRMLScene::BatchProcessState); + if (!parameterNode->GetRealTime()) + { + scene->StartState(vtkMRMLScene::BatchProcessState); + } // Get subject hierarchy item for the dose volume - vtkIdType doseShItemID = shNode->GetItemByDataNode(doseVolumeNode); - if (!doseShItemID) + vtkIdType doseShItemID = 0 + if (!parameterNode->GetRealTime()) { - vtkErrorMacro("CreateIsodoseSurfaces: Failed to get subject hierarchy item for dose volume '" << doseVolumeNode->GetName() << "'"); - return false; + doseShItemID = shNode->GetItemByDataNode(doseVolumeNode); + if (!doseShItemID) + { + vtkErrorMacro("CreateIsodoseSurfaces: Failed to get subject hierarchy item for dose volume '" << doseVolumeNode->GetName() << "'"); + return false; + } } // Check if that absolute of relative values @@ -705,9 +712,13 @@ bool vtkSlicerIsodoseModuleLogic::CreateIsodoseSurfaces(vtkMRMLIsodoseNode* para vtkSmartPointer reslicedDoseVolumeImage = reslice->GetOutput(); // Report progress - ++currentProgressStep; - double progress = (double)(currentProgressStep) / (double)progressStepCount; - this->InvokeEvent(vtkSlicerRtCommon::ProgressUpdated, (void*)&progress); + double progress = 0.0; + if (!parameterNode->GetRealTime()) + { + ++currentProgressStep; + progress = (double)(currentProgressStep) / (double)progressStepCount; + this->InvokeEvent(vtkSlicerRtCommon::ProgressUpdated, (void*)&progress); + } // reference value for relative representation double referenceValue = parameterNode->GetReferenceDoseValue(); @@ -788,10 +799,13 @@ bool vtkSlicerIsodoseModuleLogic::CreateIsodoseSurfaces(vtkMRMLIsodoseNode* para append->AddInputData(isoSurface); } - // Report progress - ++currentProgressStep; - progress = (double)(currentProgressStep) / (double)progressStepCount; - this->InvokeEvent(vtkSlicerRtCommon::ProgressUpdated, (void*)&progress); + if (!parameterNode->GetRealTime()) + { + // Report progress + ++currentProgressStep; + progress = (double)(currentProgressStep) / (double)progressStepCount; + this->InvokeEvent(vtkSlicerRtCommon::ProgressUpdated, (void*)&progress); + } } // For all isodose levels // update appended isosurfaces @@ -842,14 +856,27 @@ bool vtkSlicerIsodoseModuleLogic::CreateIsodoseSurfaces(vtkMRMLIsodoseNode* para isodoseModelNode->SetAndObservePolyData(isoSurfaces); // Put the new node as a child of dose volume - vtkIdType isodoseModelItemID = shNode->GetItemByDataNode(isodoseModelNode); - if (isodoseModelItemID) // There is no automatic SH creation in automatic tests + if (!parameterNode->GetRealTime()) { - shNode->SetItemParent(isodoseModelItemID, doseShItemID); + vtkIdType isodoseModelItemID = shNode->GetItemByDataNode(isodoseModelNode); + if (isodoseModelItemID) // There is no automatic SH creation in automatic tests + { + shNode->SetItemParent(isodoseModelItemID, doseShItemID); + } + + // Update dose color table based on isodose + this->UpdateDoseColorTableFromIsodose(parameterNode); + } + } + else if (parameterNode->GetRealTime()) + { + // In real-time mode we need to clear the polydata when there is no output + vtkMRMLModelNode* isodoseModelNode = parameterNode->GetIsosurfacesModelNode(); + if (isodoseModelNode) + { + vtkNew emptyPolyData; + isodoseModelNode->SetAndObservePolyData(emptyPolyData); } - - // Update dose color table based on isodose - this->UpdateDoseColorTableFromIsodose(parameterNode); } else { @@ -857,7 +884,11 @@ bool vtkSlicerIsodoseModuleLogic::CreateIsodoseSurfaces(vtkMRMLIsodoseNode* para res = false; } - scene->EndState(vtkMRMLScene::BatchProcessState); + if (!parameterNode->GetRealTime()) + { + scene->EndState(vtkMRMLScene::BatchProcessState); + } + return res; } diff --git a/Isodose/MRML/vtkMRMLIsodoseNode.cxx b/Isodose/MRML/vtkMRMLIsodoseNode.cxx index ef84176d..f2bd6a63 100644 --- a/Isodose/MRML/vtkMRMLIsodoseNode.cxx +++ b/Isodose/MRML/vtkMRMLIsodoseNode.cxx @@ -12,8 +12,8 @@ See the License for the specific language governing permissions and limitations under the License. - This file was originally developed by Kevin Wang, Princess Margaret Cancer Centre - and was supported by Cancer Care Ontario (CCO)'s ACRU program + This file was originally developed by Kevin Wang, Princess Margaret Cancer Centre + and was supported by Cancer Care Ontario (CCO)'s ACRU program with funds provided by the Ontario Ministry of Health and Long-Term Care and Ontario Consortium for Adaptive Interventions in Radiation Oncology (OCAIRO). @@ -49,13 +49,6 @@ vtkMRMLNodeNewMacro(vtkMRMLIsodoseNode); //---------------------------------------------------------------------------- vtkMRMLIsodoseNode::vtkMRMLIsodoseNode() { - this->ShowIsodoseLines = true; - this->ShowIsodoseSurfaces = true; - this->ShowDoseVolumesOnly = true; - this->DoseUnits = DoseUnitsType::Unknown; - this->ReferenceDoseValue = -1.; - this->RelativeRepresentationFlag = false; - this->HideFromEditors = false; } @@ -75,8 +68,9 @@ void vtkMRMLIsodoseNode::WriteXML(ostream& of, int nIndent) vtkMRMLWriteXMLEnumMacro(DoseUnits, DoseUnits); vtkMRMLWriteXMLFloatMacro(ReferenceDoseValue, ReferenceDoseValue); vtkMRMLWriteXMLBooleanMacro(RelativeRepresentationFlag, RelativeRepresentationFlag); + vtkMRMLWriteXMLBooleanMacro(RealTime, RealTime); - vtkMRMLWriteXMLEndMacro(); + vtkMRMLWriteXMLEndMacro(); } //---------------------------------------------------------------------------- @@ -92,6 +86,7 @@ void vtkMRMLIsodoseNode::ReadXMLAttributes(const char** atts) vtkMRMLReadXMLEnumMacro(DoseUnits, DoseUnits); vtkMRMLReadXMLFloatMacro(ReferenceDoseValue, ReferenceDoseValue); vtkMRMLReadXMLBooleanMacro(RelativeRepresentationFlag, RelativeRepresentationFlag); + vtkMRMLReadXMLBooleanMacro(RealTime, RealTime); vtkMRMLReadXMLEndMacro(); this->EndModify(disabledModify); @@ -113,6 +108,7 @@ void vtkMRMLIsodoseNode::Copy(vtkMRMLNode *anode) vtkMRMLCopyEnumMacro(DoseUnits); vtkMRMLCopyFloatMacro(ReferenceDoseValue); vtkMRMLCopyBooleanMacro(RelativeRepresentationFlag); + vtkMRMLCopyBooleanMacro(RealTime); vtkMRMLCopyEndMacro(); this->EndModify(disabledModify); @@ -130,6 +126,7 @@ void vtkMRMLIsodoseNode::PrintSelf(ostream& os, vtkIndent indent) vtkMRMLPrintEnumMacro(DoseUnits); vtkMRMLPrintFloatMacro(ReferenceDoseValue); vtkMRMLPrintBooleanMacro(RelativeRepresentationFlag); + vtkMRMLPrintBooleanMacro(RealTime); vtkMRMLPrintEndMacro(); } diff --git a/Isodose/MRML/vtkMRMLIsodoseNode.h b/Isodose/MRML/vtkMRMLIsodoseNode.h index a1b7fae2..64ac0ece 100644 --- a/Isodose/MRML/vtkMRMLIsodoseNode.h +++ b/Isodose/MRML/vtkMRMLIsodoseNode.h @@ -12,8 +12,8 @@ See the License for the specific language governing permissions and limitations under the License. - This file was originally developed by Kevin Wang, Princess Margaret Cancer Centre - and was supported by Cancer Care Ontario (CCO)'s ACRU program + This file was originally developed by Kevin Wang, Princess Margaret Cancer Centre + and was supported by Cancer Care Ontario (CCO)'s ACRU program with funds provided by the Ontario Ministry of Health and Long-Term Care and Ontario Consortium for Adaptive Interventions in Radiation Oncology (OCAIRO). @@ -44,19 +44,19 @@ class VTK_SLICER_ISODOSE_MODULE_MRML_EXPORT vtkMRMLIsodoseNode : public vtkMRMLN vtkTypeMacro(vtkMRMLIsodoseNode, vtkMRMLNode); void PrintSelf(ostream& os, vtkIndent indent) override; - /// Create instance of a GAD node. + /// Create instance of a GAD node. vtkMRMLNode* CreateNodeInstance() override; - /// Set node attributes from name/value pairs + /// Set node attributes from name/value pairs void ReadXMLAttributes(const char** atts) override; - /// Write this node's information to a MRML file in XML format. + /// Write this node's information to a MRML file in XML format. void WriteXML(ostream& of, int indent) override; - /// Copy the node's attributes to this object + /// Copy the node's attributes to this object void Copy(vtkMRMLNode *node) override; - /// Get unique node XML tag name (like Volume, Model) + /// Get unique node XML tag name (like Volume, Model) const char* GetNodeTagName() override { return "Isodose"; }; public: @@ -79,29 +79,47 @@ class VTK_SLICER_ISODOSE_MODULE_MRML_EXPORT vtkMRMLIsodoseNode : public vtkMRMLN vtkGetMacro(ShowIsodoseLines, bool); vtkSetMacro(ShowIsodoseLines, bool); vtkBooleanMacro(ShowIsodoseLines, bool); + //@} + //@{ /// Get/Set show isodose surfaces checkbox state vtkGetMacro(ShowIsodoseSurfaces, bool); vtkSetMacro(ShowIsodoseSurfaces, bool); vtkBooleanMacro(ShowIsodoseSurfaces, bool); + //@} + //@{ /// Get/Set show dose volumes only checkbox state vtkGetMacro(ShowDoseVolumesOnly, bool); vtkSetMacro(ShowDoseVolumesOnly, bool); vtkBooleanMacro(ShowDoseVolumesOnly, bool); + //@} + //@{ /// Get/Set reference dose value vtkGetMacro(ReferenceDoseValue, double); vtkSetMacro(ReferenceDoseValue, double); + //@} + //@{ /// Get/Set dose units type vtkGetMacro(DoseUnits, DoseUnitsType); vtkSetMacro(DoseUnits, DoseUnitsType); + //@} + //@{ /// Get/Set relative representation flag vtkGetMacro(RelativeRepresentationFlag, bool); vtkSetMacro(RelativeRepresentationFlag, bool); vtkBooleanMacro(RelativeRepresentationFlag, bool); + //@} + + //@{ + /// Get/Set real time flag + vtkGetMacro(RealTime, bool); + vtkSetMacro(RealTime, bool); + vtkBooleanMacro(RealTime, bool); + //@} protected: vtkMRMLIsodoseNode(); @@ -114,23 +132,30 @@ class VTK_SLICER_ISODOSE_MODULE_MRML_EXPORT vtkMRMLIsodoseNode : public vtkMRMLN void SetDoseUnits(int id); /// State of Show isodose lines checkbox - bool ShowIsodoseLines; + bool ShowIsodoseLines{true}; /// State of Show isodose surfaces checkbox - bool ShowIsodoseSurfaces; + bool ShowIsodoseSurfaces{true}; /// State of Show dose volumes only checkbox - bool ShowDoseVolumesOnly; + bool ShowDoseVolumesOnly{true}; /// Type of dose units - DoseUnitsType DoseUnits; + DoseUnitsType DoseUnits{DoseUnitsType::Unknown}; /// Reference dose value - double ReferenceDoseValue; + double ReferenceDoseValue{-1.}; /// Whether use relative isolevels representation /// for absolute dose (Gy) and unknown units or not - bool RelativeRepresentationFlag; + bool RelativeRepresentationFlag{false}; + + /// Flag supporting real time applications, when there is strictly one set of isodose surfaces. + /// When this flag is enabled, the following functions are prevented: use of subject hierarchy to organize the isodose + /// model nodes, reporting of progress, batch processing, and update of dose color table from the isodose one. + /// Instead, top-level isodose model nodes are re-used (by node name) at every computation. It is useful when isodose is needed + /// to be computed on-the-fly for streamed dose data, when one set of isodose surfaces is all that is needed to be kept and displayed. + bool RealTime{false}; }; #endif