diff --git a/Libs/MRML/Core/Testing/vtkMRMLSliceNodeTest1.cxx b/Libs/MRML/Core/Testing/vtkMRMLSliceNodeTest1.cxx index 392a3edaf92..25ea2928830 100644 --- a/Libs/MRML/Core/Testing/vtkMRMLSliceNodeTest1.cxx +++ b/Libs/MRML/Core/Testing/vtkMRMLSliceNodeTest1.cxx @@ -32,6 +32,9 @@ int GetSliceOrientationPresetTest(); int GetSliceOrientationPresetNameTest(); int SetOrientationTest(); int InitializeDefaultMatrixTest(); +int SlabReconstructionEnabledTest(); +int SlabReconstructionTypeTest(); +int SlabReconstructionThicknessTest(); //---------------------------------------------------------------------------- int vtkMRMLSliceNodeTest1(int , char * [] ) @@ -48,6 +51,9 @@ int vtkMRMLSliceNodeTest1(int , char * [] ) CHECK_EXIT_SUCCESS(GetSliceOrientationPresetNameTest()); CHECK_EXIT_SUCCESS(SetOrientationTest()); CHECK_EXIT_SUCCESS(InitializeDefaultMatrixTest()); + CHECK_EXIT_SUCCESS(SlabReconstructionEnabledTest()); + CHECK_EXIT_SUCCESS(SlabReconstructionTypeTest()); + CHECK_EXIT_SUCCESS(SlabReconstructionThicknessTest()); return EXIT_SUCCESS; } @@ -426,3 +432,87 @@ int InitializeDefaultMatrixTest() return EXIT_SUCCESS; } + +//---------------------------------------------------------------------------- +int SlabReconstructionEnabledTest() +{ + vtkNew sliceNode; + + CHECK_BOOL(sliceNode->GetSlabReconstructionEnabled(), false); + + // Set using set macro + { + sliceNode->SetSlabReconstructionEnabled(true); + CHECK_BOOL(sliceNode->GetSlabReconstructionEnabled(), true); + } + + // Set using on/off macro + { + sliceNode->SlabReconstructionEnabledOn(); + CHECK_BOOL(sliceNode->GetSlabReconstructionEnabled(), true); + sliceNode->SlabReconstructionEnabledOff(); + CHECK_BOOL(sliceNode->GetSlabReconstructionEnabled(), false); + } + + return EXIT_SUCCESS; +} + +//---------------------------------------------------------------------------- +int SlabReconstructionTypeTest() +{ + vtkNew sliceNode; + + CHECK_INT(sliceNode->GetSlabReconstructionType(), VTK_IMAGE_SLAB_MAX); + + // Set to min + { + sliceNode->SetSlabReconstructionType(VTK_IMAGE_SLAB_MIN); + CHECK_INT(sliceNode->GetSlabReconstructionType(), VTK_IMAGE_SLAB_MIN); + } + + // Set to mean + { + sliceNode->SetSlabReconstructionType(VTK_IMAGE_SLAB_MEAN); + CHECK_INT(sliceNode->GetSlabReconstructionType(), VTK_IMAGE_SLAB_MEAN); + } + + // Set to sum + { + sliceNode->SetSlabReconstructionType(VTK_IMAGE_SLAB_SUM); + CHECK_INT(sliceNode->GetSlabReconstructionType(), VTK_IMAGE_SLAB_SUM); + } + + // Check GetSlabReconstructionTypeAsString + { + CHECK_STRING(sliceNode->GetSlabReconstructionTypeAsString(VTK_IMAGE_SLAB_MAX), "Max"); + CHECK_STRING(sliceNode->GetSlabReconstructionTypeAsString(VTK_IMAGE_SLAB_MIN), "Min"); + CHECK_STRING(sliceNode->GetSlabReconstructionTypeAsString(VTK_IMAGE_SLAB_MEAN), "Mean"); + CHECK_STRING(sliceNode->GetSlabReconstructionTypeAsString(VTK_IMAGE_SLAB_SUM), "Sum"); + } + + // Check GetSlabReconstructionTypeFromString + { + CHECK_INT(sliceNode->GetSlabReconstructionTypeFromString("Max"), VTK_IMAGE_SLAB_MAX); + CHECK_INT(sliceNode->GetSlabReconstructionTypeFromString("Min"), VTK_IMAGE_SLAB_MIN); + CHECK_INT(sliceNode->GetSlabReconstructionTypeFromString("Mean"), VTK_IMAGE_SLAB_MEAN); + CHECK_INT(sliceNode->GetSlabReconstructionTypeFromString("Sum"), VTK_IMAGE_SLAB_SUM); + } + + return EXIT_SUCCESS; +} + +//---------------------------------------------------------------------------- +int SlabReconstructionThicknessTest() +{ + vtkNew sliceNode; + + CHECK_DOUBLE(sliceNode->GetSlabReconstructionThickness(), 1.); + + // Set using set macro + { + sliceNode->SetSlabReconstructionThickness(99.5); + CHECK_DOUBLE(sliceNode->GetSlabReconstructionThickness(), 99.5); + } + + return EXIT_SUCCESS; +} diff --git a/Libs/MRML/Core/vtkMRMLSliceNode.cxx b/Libs/MRML/Core/vtkMRMLSliceNode.cxx index d95b800a77e..3f1ba2d557e 100644 --- a/Libs/MRML/Core/vtkMRMLSliceNode.cxx +++ b/Libs/MRML/Core/vtkMRMLSliceNode.cxx @@ -84,6 +84,11 @@ vtkMRMLSliceNode::vtkMRMLSliceNode() this->SliceResolutionMode = vtkMRMLSliceNode::SliceResolutionMatch2DView; + this->SlabReconstructionEnabled = false; + this->SlabReconstructionType = VTK_IMAGE_SLAB_MAX; + this->SlabReconstructionThickness = 1.; + this->SlabReconstructionOversamplingFactor = 2.0; + this->XYZOrigin[0] = 0; this->XYZOrigin[1] = 0; this->XYZOrigin[2] = 0; @@ -905,6 +910,11 @@ void vtkMRMLSliceNode::WriteXML(ostream& of, int nIndent) vtkMRMLWriteXMLStdStringVectorMacro(threeDViewNodeRef, ThreeDViewIDs, std::vector); } + vtkMRMLWriteXMLBooleanMacro(slabReconstructionEnabled, SlabReconstructionEnabled); + vtkMRMLWriteXMLEnumMacro(slabReconstructionType, SlabReconstructionType); + vtkMRMLWriteXMLFloatMacro(slabReconstructionThickness, SlabReconstructionThickness); + vtkMRMLWriteXMLFloatMacro(slabReconstructionOversamplingFactor, SlabReconstructionOversamplingFactor); + vtkMRMLWriteXMLEndMacro(); } @@ -992,6 +1002,11 @@ void vtkMRMLSliceNode::ReadXMLAttributes(const char** atts) } } + vtkMRMLReadXMLBooleanMacro(slabReconstructionEnabled, SlabReconstructionEnabled); + vtkMRMLReadXMLEnumMacro(slabReconstructionType, SlabReconstructionType); + vtkMRMLReadXMLFloatMacro(slabReconstructionThickness, SlabReconstructionThickness); + vtkMRMLReadXMLFloatMacro(slabReconstructionOversamplingFactor, SlabReconstructionOversamplingFactor); + vtkMRMLReadXMLEndMacro(); if (!layoutColorFound) @@ -1105,6 +1120,11 @@ void vtkMRMLSliceNode::CopyContent(vtkMRMLNode* anode, bool deepCopy/*=true*/) vtkMRMLCopyVectorMacro(UVWMaximumDimensions, int, 3); vtkMRMLCopyVectorMacro(PrescribedSliceSpacing, double, 3); + vtkMRMLCopyBooleanMacro(SlabReconstructionEnabled); + vtkMRMLCopyEnumMacro(SlabReconstructionType); + vtkMRMLCopyFloatMacro(SlabReconstructionThickness); + vtkMRMLCopyFloatMacro(SlabReconstructionOversamplingFactor); + vtkMRMLCopyEndMacro(); this->UpdateMatrices(); @@ -1199,6 +1219,11 @@ void vtkMRMLSliceNode::PrintSelf(ostream& os, vtkIndent indent) vtkMRMLPrintStringMacro(DefaultOrientation); + vtkMRMLPrintBooleanMacro(SlabReconstructionEnabled); + vtkMRMLPrintEnumMacro(SlabReconstructionType); + vtkMRMLPrintFloatMacro(SlabReconstructionThickness); + vtkMRMLPrintFloatMacro(SlabReconstructionOversamplingFactor); + vtkMRMLPrintEndMacro(); } @@ -2024,3 +2049,39 @@ bool vtkMRMLSliceNode::SetOrientationToDefault() } return this->SetOrientation(this->GetDefaultOrientation()); } + +//--------------------------------------------------------------------------- +const char* vtkMRMLSliceNode::GetSlabReconstructionTypeAsString(int slabReconstructionType) +{ + switch (slabReconstructionType) + { + case VTK_IMAGE_SLAB_MAX: return "Max"; + case VTK_IMAGE_SLAB_MIN: return "Min"; + case VTK_IMAGE_SLAB_MEAN: return "Mean"; + case VTK_IMAGE_SLAB_SUM: return "Sum"; + default: + vtkGenericWarningMacro("Unknown reconstruction type: " << slabReconstructionType); + return ""; + } +} + +//----------------------------------------------------------- +int vtkMRMLSliceNode::GetSlabReconstructionTypeFromString(const char* name) +{ + if (name == nullptr) + { + // invalid name + return -1; + } + // VTK_IMAGE_SLAB enum doesn't use last + for (int ii = 0; ii < 4; ii++) + { + if (strcmp(name, GetSlabReconstructionTypeAsString(ii)) == 0) + { + // found a matching name + return ii; + } + } + // unknown name + return -1; +} diff --git a/Libs/MRML/Core/vtkMRMLSliceNode.h b/Libs/MRML/Core/vtkMRMLSliceNode.h index 2f6ce4cb971..8f0567e0076 100644 --- a/Libs/MRML/Core/vtkMRMLSliceNode.h +++ b/Libs/MRML/Core/vtkMRMLSliceNode.h @@ -523,6 +523,33 @@ class VTK_MRML_EXPORT vtkMRMLSliceNode : public vtkMRMLAbstractViewNode virtual void SetSliceResolutionMode(int mode); vtkGetMacro(SliceResolutionMode, int); + /// @{ + /// Get/set the slab reconstruction visibility. + vtkGetMacro(SlabReconstructionEnabled, bool); + vtkSetMacro(SlabReconstructionEnabled, bool); + vtkBooleanMacro(SlabReconstructionEnabled, bool); + /// @} + + /// @{ + /// Get/set the slab reconstruction type. + vtkGetMacro(SlabReconstructionType, int); + vtkSetMacro(SlabReconstructionType, int); + static const char* GetSlabReconstructionTypeAsString(int slabReconstructionType); + static int GetSlabReconstructionTypeFromString(const char* name); + /// @} + + /// @{ + /// Get/set the slab reconstruction thickness in physical unit. + vtkGetMacro(SlabReconstructionThickness, double); + vtkSetMacro(SlabReconstructionThickness, double); + /// @} + + /// @{ + /// Get/set the slab reconstruction oversampling factor. + vtkGetMacro(SlabReconstructionOversamplingFactor, double); + vtkSetMacro(SlabReconstructionOversamplingFactor, double); + /// @} + protected: vtkMRMLSliceNode(); ~vtkMRMLSliceNode() override; @@ -558,6 +585,11 @@ class VTK_MRML_EXPORT vtkMRMLSliceNode : public vtkMRMLAbstractViewNode int UVWDimensions[3]; int UVWMaximumDimensions[3]; + bool SlabReconstructionEnabled; + int SlabReconstructionType; + double SlabReconstructionThickness; + double SlabReconstructionOversamplingFactor; + // Hold the string returned by GetOrientationString std::string OrientationString; diff --git a/Libs/MRML/Logic/vtkMRMLSliceLogic.cxx b/Libs/MRML/Logic/vtkMRMLSliceLogic.cxx index 17787f5c1d6..1870fbc4cc0 100644 --- a/Libs/MRML/Logic/vtkMRMLSliceLogic.cxx +++ b/Libs/MRML/Logic/vtkMRMLSliceLogic.cxx @@ -466,6 +466,10 @@ void vtkMRMLSliceLogic::OnMRMLNodeModified(vtkMRMLNode* node) sliceDisplayNode->SetVisibility( this->SliceNode->GetSliceVisible() ); sliceDisplayNode->SetViewNodeIDs( this->SliceNode->GetThreeDViewIDs()); } + + vtkMRMLSliceLogic::UpdateReconstructionSlab(this, this->GetBackgroundLayer()); + vtkMRMLSliceLogic::UpdateReconstructionSlab(this, this->GetForegroundLayer()); + } else if (node == this->SliceCompositeNode) { @@ -968,6 +972,42 @@ bool vtkMRMLSliceLogic::UpdateFractions(vtkImageMathematics* fraction, double op return modified; } +//---------------------------------------------------------------------------- +void vtkMRMLSliceLogic::UpdateReconstructionSlab(vtkMRMLSliceLogic* sliceLogic, vtkMRMLSliceLayerLogic* sliceLayerLogic) +{ + if (!sliceLogic || !sliceLayerLogic || !sliceLogic->GetSliceNode()) + { + return; + } + + vtkImageReslice* reslice = sliceLayerLogic->GetReslice(); + vtkMRMLSliceNode* sliceNode = sliceLayerLogic->GetSliceNode(); + + double sliceSpacing; + if (sliceNode->GetSliceSpacingMode() == vtkMRMLSliceNode::PrescribedSliceSpacingMode) + { + sliceSpacing = sliceNode->GetPrescribedSliceSpacing()[2]; + } + else + { + sliceSpacing = sliceLogic->GetLowestVolumeSliceSpacing()[2]; + } + + int slabNumberOfSlices = 1; + if (sliceNode->GetSlabReconstructionEnabled() + && sliceSpacing > 0 + && sliceNode->GetSlabReconstructionThickness() > sliceSpacing + ) + { + slabNumberOfSlices = static_cast(sliceNode->GetSlabReconstructionThickness() / sliceSpacing); + } + reslice->SetSlabNumberOfSlices(slabNumberOfSlices); + + reslice->SetSlabMode(sliceNode->GetSlabReconstructionType()); + + double slabSliceSpacingFraction = sliceSpacing / sliceNode->GetSlabReconstructionOversamplingFactor(); + reslice->SetSlabSliceSpacingFraction(slabSliceSpacingFraction); +} //---------------------------------------------------------------------------- void vtkMRMLSliceLogic::UpdatePipeline() diff --git a/Libs/MRML/Logic/vtkMRMLSliceLogic.h b/Libs/MRML/Logic/vtkMRMLSliceLogic.h index 103cdd2405b..13ca998fb2e 100644 --- a/Libs/MRML/Logic/vtkMRMLSliceLogic.h +++ b/Libs/MRML/Logic/vtkMRMLSliceLogic.h @@ -430,6 +430,9 @@ class VTK_MRML_LOGIC_EXPORT vtkMRMLSliceLogic : public vtkMRMLAbstractLogic /// Helper to update foreground opacity when adding/subtracting the background layer bool UpdateFractions(vtkImageMathematics* fraction, double opacity); + /// Helper to update reconstruction slab settings for a given layer. + static void UpdateReconstructionSlab(vtkMRMLSliceLogic* sliceLogic, vtkMRMLSliceLayerLogic* sliceLayerLogic); + /// Returns true if position is inside the selected layer volume. /// Use background flag to choose between foreground/background layer. bool IsEventInsideVolume(bool background, double worldPos[3]); diff --git a/Libs/MRML/Widgets/Resources/Icons/SlabReconstruction.png b/Libs/MRML/Widgets/Resources/Icons/SlabReconstruction.png new file mode 100644 index 00000000000..06094708452 Binary files /dev/null and b/Libs/MRML/Widgets/Resources/Icons/SlabReconstruction.png differ diff --git a/Libs/MRML/Widgets/Resources/UI/qMRMLSliceControllerWidget.ui b/Libs/MRML/Widgets/Resources/UI/qMRMLSliceControllerWidget.ui index ef602ed7993..77e64801e63 100644 --- a/Libs/MRML/Widgets/Resources/UI/qMRMLSliceControllerWidget.ui +++ b/Libs/MRML/Widgets/Resources/UI/qMRMLSliceControllerWidget.ui @@ -626,6 +626,23 @@ + + + + Show slab reconstruction + + + QToolButton::InstantPopup + + + + :/Icons/SlabReconstruction.png:/Icons/SlabReconstruction.png + + + true + + + @@ -754,6 +771,14 @@ Show reformat widget + + + true + + + Show Slab Reconstruction + + true @@ -1168,6 +1193,50 @@ Yellow ruler + + + true + + + Max + + + Set slab reconstruction type to Max + + + + + true + + + Min + + + Set slab reconstruction type to Min + + + + + true + + + Mean + + + Set slab reconstruction type to Mean + + + + + true + + + Sum + + + Set slab reconstruction type to Sum + + @@ -1712,5 +1781,11 @@ + + MoreButton + toggled(bool) + ShowSlabReconstructionButton + setVisible(bool) + diff --git a/Libs/MRML/Widgets/Resources/qMRMLWidgets.qrc b/Libs/MRML/Widgets/Resources/qMRMLWidgets.qrc index e1eafc07705..dd0afc6531c 100644 --- a/Libs/MRML/Widgets/Resources/qMRMLWidgets.qrc +++ b/Libs/MRML/Widgets/Resources/qMRMLWidgets.qrc @@ -161,5 +161,6 @@ Icons/LayoutFourUpQuantitativeTableView.png Icons/ViewMaximize.png Icons/ViewRestore.png + Icons/SlabReconstruction.png diff --git a/Libs/MRML/Widgets/qMRMLSliceControllerWidget.cxx b/Libs/MRML/Widgets/qMRMLSliceControllerWidget.cxx index d2268318742..dc7fc0e87ec 100644 --- a/Libs/MRML/Widgets/qMRMLSliceControllerWidget.cxx +++ b/Libs/MRML/Widgets/qMRMLSliceControllerWidget.cxx @@ -63,6 +63,7 @@ #include #include #include +#include //-------------------------------------------------------------------------- // qMRMLSliceViewPrivate methods @@ -92,6 +93,7 @@ qMRMLSliceControllerWidgetPrivate::qMRMLSliceControllerWidgetPrivate(qMRMLSliceC this->LabelMapMenu = nullptr; this->OrientationMarkerMenu = nullptr; this->RulerMenu = nullptr; + this->SlabReconstructionMenu = nullptr; this->SliceSpacingSpinBox = nullptr; this->SliceFOVSpinBox = nullptr; @@ -235,6 +237,10 @@ void qMRMLSliceControllerWidgetPrivate::setupPopupUi() q, SLOT(setLightboxTo3x3())); QObject::connect(this->actionLightbox6x6_view, SIGNAL(triggered()), q, SLOT(setLightboxTo6x6())); + + QObject::connect(this->actionShow_slab_reconstruction_widget, SIGNAL(toggled(bool)), + q, SLOT(showSlabReconstructionWidget(bool))); + this->setupLightboxMenu(); this->setupCompositingMenu(); this->setupSliceSpacingMenu(); @@ -243,6 +249,7 @@ void qMRMLSliceControllerWidgetPrivate::setupPopupUi() this->setupLabelMapMenu(); this->setupOrientationMarkerMenu(); this->setupRulerMenu(); + this->setupSlabReconstructionMenu(); // Visibility column this->connect(this->actionSegmentationVisibility, SIGNAL(triggered(bool)), @@ -334,6 +341,7 @@ void qMRMLSliceControllerWidgetPrivate::setupPopupUi() this->LightBoxToolButton->setMenu(this->LightboxMenu); this->ShowReformatWidgetToolButton->setDefaultAction(this->actionShow_reformat_widget); + this->ShowSlabReconstructionButton->setMenu(this->SlabReconstructionMenu); this->SliceCompositeButton->setMenu(this->CompositingMenu); this->SliceSpacingButton->setMenu(this->SliceSpacingMenu); this->SliceVisibilityButton->setMenu(this->SliceModelMenu); @@ -907,6 +915,10 @@ void qMRMLSliceControllerWidgetPrivate::updateWidgetFromMRMLSliceNode() this->actionShow_reformat_widget->setChecked(showReformat); this->actionShow_reformat_widget->setText( showReformat ? tr("Hide reformat widget"): tr("Show reformat widget")); + // Reconstruction + bool showSlabReconstruction = sliceNode->GetSlabReconstructionEnabled(); + this->actionShow_slab_reconstruction_widget->setChecked(showSlabReconstruction); + this->SlabReconstructionThicknessSpinBox->setValue(sliceNode->GetSlabReconstructionThickness()); // Slice spacing mode this->SliceSpacingButton->setIcon( sliceNode->GetSliceSpacingMode() == vtkMRMLSliceNode::AutomaticSliceSpacingMode ? @@ -1005,6 +1017,13 @@ void qMRMLSliceControllerWidgetPrivate::updateWidgetFromMRMLSliceNode() { action->setChecked(true); } + + // Slab reconstruction type (check the selected option) + action = qobject_cast(this->SlabReconstructionTypesMapper->mapping(sliceNode->GetSlabReconstructionType())); + if (action) + { + action->setChecked(true); + } } // -------------------------------------------------------------------------- @@ -1101,6 +1120,14 @@ void qMRMLSliceControllerWidgetPrivate::updateWidgetFromUnitNode() { Q_Q(qMRMLSliceControllerWidget); q->setSliceOffsetResolution(q->sliceOffsetResolution()); + if (this->SelectionNode && q->mrmlScene()) + { + vtkMRMLUnitNode* unitNode = vtkMRMLUnitNode::SafeDownCast(q->mrmlScene()->GetNodeByID(this->SelectionNode->GetUnitNodeID("length"))); + if (unitNode) + { + this->SlabReconstructionThicknessSpinBox->setSuffix(unitNode->GetSuffix()); + } + } } // -------------------------------------------------------------------------- @@ -1548,6 +1575,48 @@ void qMRMLSliceControllerWidgetPrivate::setupRulerMenu() rulerMenu->addActions(rulerColorActions->actions()); } +// -------------------------------------------------------------------------- +void qMRMLSliceControllerWidgetPrivate::setupSlabReconstructionMenu() +{ + Q_Q(qMRMLSliceControllerWidget);// Menu + this->SlabReconstructionMenu = new QMenu(tr("Slab Reconstruction"), this->ShowSlabReconstructionButton); + this->SlabReconstructionMenu->addAction(this->actionShow_slab_reconstruction_widget); + this->SlabReconstructionMenu->setObjectName("slabMenu"); + + // Slab Reconstruction Thickness + QMenu* slabReconstructionThickness = new QMenu(tr("Slab Thickness"), this->ShowSlabReconstructionButton); + slabReconstructionThickness->setObjectName("slicerSpacingManualMode"); + this->SlabReconstructionThicknessSpinBox = new ctkDoubleSpinBox(slabReconstructionThickness); + this->SliceSpacingSpinBox->setDecimals(3); + this->SlabReconstructionThicknessSpinBox->setRange(1., VTK_FLOAT_MAX); + this->SlabReconstructionThicknessSpinBox->setSingleStep(0.1); + this->SlabReconstructionThicknessSpinBox->setValue(1.); + QObject::connect(this->SlabReconstructionThicknessSpinBox, SIGNAL(valueChanged(double)), + q, SLOT(setSlabReconstructionThickness(double))); + QWidgetAction* slabReconstructionThicknessAction = new QWidgetAction(slabReconstructionThickness); + slabReconstructionThicknessAction->setDefaultWidget(this->SlabReconstructionThicknessSpinBox); + slabReconstructionThickness->addAction(slabReconstructionThicknessAction); + this->SlabReconstructionMenu->addMenu(slabReconstructionThickness); + + // Slab Reconstruction Type + this->SlabReconstructionTypesMapper = new ctkSignalMapper(this->SlabReconstructionMenu); + this->SlabReconstructionTypesMapper->setMapping(this->actionSlabReconstructionMax, VTK_IMAGE_SLAB_MAX); + this->SlabReconstructionTypesMapper->setMapping(this->actionSlabReconstructionMin, VTK_IMAGE_SLAB_MIN); + this->SlabReconstructionTypesMapper->setMapping(this->actionSlabReconstructionMean, VTK_IMAGE_SLAB_MEAN); + this->SlabReconstructionTypesMapper->setMapping(this->actionSlabReconstructionSum, VTK_IMAGE_SLAB_SUM); + QActionGroup* slabReconstructionTypesActions = new QActionGroup(this->SlabReconstructionMenu); + slabReconstructionTypesActions->setExclusive(true); + slabReconstructionTypesActions->addAction(this->actionSlabReconstructionMax); + slabReconstructionTypesActions->addAction(this->actionSlabReconstructionMin); + slabReconstructionTypesActions->addAction(this->actionSlabReconstructionMean); + slabReconstructionTypesActions->addAction(this->actionSlabReconstructionSum); + QObject::connect(this->SlabReconstructionTypesMapper, SIGNAL(mapped(int)),q, SLOT(setSlabReconstructionType(int))); + QObject::connect(slabReconstructionTypesActions, SIGNAL(triggered(QAction*)),this->SlabReconstructionTypesMapper, SLOT(map(QAction*))); + + this->SlabReconstructionMenu->addActions(slabReconstructionTypesActions->actions()); + this->SlabReconstructionMenu->addSeparator(); +} + // -------------------------------------------------------------------------- void qMRMLSliceControllerWidgetPrivate::onSegmentVisibilitySelectionChanged(QStringList selectedSegmentIDs) { @@ -2306,6 +2375,27 @@ void qMRMLSliceControllerWidget::showReformatWidget(bool show) } } +//--------------------------------------------------------------------------- +void qMRMLSliceControllerWidget::showSlabReconstructionWidget(bool show) +{ + Q_D(qMRMLSliceControllerWidget); + + vtkSmartPointer nodes = d->saveNodesForUndo("vtkMRMLSliceNode"); + if (!nodes.GetPointer()) + { + return; + } + vtkMRMLSliceNode* node = nullptr; + vtkCollectionSimpleIterator it; + for (nodes->InitTraversal(it);(node = static_cast(nodes->GetNextItemAsObject(it)));) + { + if (node == this->mrmlSliceNode() || this->isLinked()) + { + node->SetSlabReconstructionEnabled(show); + } + } +} + //--------------------------------------------------------------------------- void qMRMLSliceControllerWidget::lockReformatWidgetToCamera(bool lock) { @@ -2787,6 +2877,46 @@ void qMRMLSliceControllerWidget::setRulerColor(int newRulerColor) } } +// -------------------------------------------------------------------------- +void qMRMLSliceControllerWidget::setSlabReconstructionType(int newSlabReconstructionType) +{ + Q_D(qMRMLSliceControllerWidget); + vtkSmartPointer nodes = d->saveNodesForUndo("vtkMRMLSliceNode"); + if (!nodes.GetPointer()) + { + return; + } + vtkMRMLSliceNode* node = nullptr; + vtkCollectionSimpleIterator it; + for (nodes->InitTraversal(it);(node = static_cast(nodes->GetNextItemAsObject(it)));) + { + if (node == this->mrmlSliceNode() || this->isLinked()) + { + node->SetSlabReconstructionType(newSlabReconstructionType); + } + } +} + +// -------------------------------------------------------------------------- +void qMRMLSliceControllerWidget::setSlabReconstructionThickness(double thickness) +{ + Q_D(qMRMLSliceControllerWidget); + vtkSmartPointer nodes = d->saveNodesForUndo("vtkMRMLSliceNode"); + if (!nodes.GetPointer()) + { + return; + } + vtkMRMLSliceNode* node = nullptr; + vtkCollectionSimpleIterator it; + for (nodes->InitTraversal(it);(node = static_cast(nodes->GetNextItemAsObject(it)));) + { + if (node == this->mrmlSliceNode() || this->isLinked()) + { + node->SetSlabReconstructionThickness(thickness); + } + } +} + // -------------------------------------------------------------------------- void qMRMLSliceControllerWidget::updateSegmentationControlsVisibility() { diff --git a/Libs/MRML/Widgets/qMRMLSliceControllerWidget.h b/Libs/MRML/Widgets/qMRMLSliceControllerWidget.h index d416702daa0..921f8fc3b5b 100644 --- a/Libs/MRML/Widgets/qMRMLSliceControllerWidget.h +++ b/Libs/MRML/Widgets/qMRMLSliceControllerWidget.h @@ -233,6 +233,8 @@ public slots: /// Reformat widget void showReformatWidget(bool show); void lockReformatWidgetToCamera(bool lock); + /// Reconstruction widget + void showSlabReconstructionWidget(bool show); /// Compositing void setCompositing(int mode); void setCompositingToAlphaBlend(); @@ -272,6 +274,10 @@ public slots: void setRulerType(int type); void setRulerColor(int color); + // Slab Reconstruction + void setSlabReconstructionType(int type); + void setSlabReconstructionThickness(double thickness); + // Lightbox void setLightbox(int rows, int columns); void setLightboxTo1x1(); diff --git a/Libs/MRML/Widgets/qMRMLSliceControllerWidget_p.h b/Libs/MRML/Widgets/qMRMLSliceControllerWidget_p.h index ab7b6070fb0..997c8725a0d 100644 --- a/Libs/MRML/Widgets/qMRMLSliceControllerWidget_p.h +++ b/Libs/MRML/Widgets/qMRMLSliceControllerWidget_p.h @@ -91,6 +91,7 @@ class QMRML_WIDGETS_EXPORT qMRMLSliceControllerWidgetPrivate void setupMoreOptionsMenu(); void setupOrientationMarkerMenu(); void setupRulerMenu(); + void setupSlabReconstructionMenu(); vtkSmartPointer saveNodesForUndo(const QString& nodeTypes); @@ -188,11 +189,13 @@ public slots: QMenu* LabelMapMenu; QMenu* OrientationMarkerMenu; QMenu* RulerMenu; + QMenu* SlabReconstructionMenu; ctkDoubleSpinBox* SliceSpacingSpinBox; ctkDoubleSpinBox* SliceFOVSpinBox; QSpinBox* LightBoxRowsSpinBox; QSpinBox* LightBoxColumnsSpinBox; + ctkDoubleSpinBox* SlabReconstructionThicknessSpinBox; ctkDoubleSpinBox* SliceModelFOVXSpinBox; ctkDoubleSpinBox* SliceModelFOVYSpinBox; @@ -211,6 +214,8 @@ public slots: ctkSignalMapper* RulerTypesMapper; ctkSignalMapper* RulerColorMapper; + ctkSignalMapper* SlabReconstructionTypesMapper; + bool ShowSliceOffsetSlider{true}; }; diff --git a/Modules/Scripted/DataProbe/DataProbeLib/SliceViewAnnotations.py b/Modules/Scripted/DataProbe/DataProbeLib/SliceViewAnnotations.py index 45b138a4238..21349e46b0d 100644 --- a/Modules/Scripted/DataProbe/DataProbeLib/SliceViewAnnotations.py +++ b/Modules/Scripted/DataProbe/DataProbeLib/SliceViewAnnotations.py @@ -70,7 +70,9 @@ def __init__(self, layoutManager=None): '4-Model': {'text': '', 'category': 'C'}, '5-Patient-Position': {'text': '', 'category': 'A'}, '6-TR': {'text': '', 'category': 'A'}, - '7-TE': {'text': '', 'category': 'A'} + '7-TE': {'text': '', 'category': 'A'}, + '8-SlabReconstructionThickness': {'text': '', 'category': 'A'}, + '9-SlabReconstructionType': {'text': '', 'category': 'A'} }) self.annotationsDisplayAmount = 0 @@ -449,6 +451,17 @@ def makeAnnotationText(self, sliceLogic): else: self.dicomVolumeNode = 0 + # Slab reconstruction is applied to both foreground and background + if self.topRight and sliceNode.GetSlabReconstructionEnabled(): + unitNode = slicer.app.applicationLogic().GetSelectionNode().GetUnitNode("length") + self.cornerTexts[3]['8-SlabReconstructionThickness']['text'] = 'Thickness: ' + str( + sliceNode.GetSlabReconstructionThickness()) + ' ' + unitNode.GetSuffix() + self.cornerTexts[3]['9-SlabReconstructionType']['text'] = 'Type: ' + sliceNode.GetSlabReconstructionTypeAsString( + sliceNode.GetSlabReconstructionType()) + else: + self.cornerTexts[3]['8-SlabReconstructionThickness']['text'] = '' + self.cornerTexts[3]['9-SlabReconstructionType']['text'] = '' + if (labelVolume is not None) and self.bottomLeft: labelOpacity = sliceCompositeNode.GetLabelOpacity() labelVolumeName = labelVolume.GetName()