Skip to content

Commit

Permalink
ENH: Update Slicer Controller adding Thick Slab Reconstruction (Slice…
Browse files Browse the repository at this point in the history
…r#7021)

This commit updates the Slice Controller widget adding controls for
enabling the Thick Slab Reconstruction (TSR), selecting the reconstruction
type (max, min, mean, sum) as well as adjusting the slab thickness.

It also updates the DataProbe/SlicerViewAnnotations to allow displaying
the slab type and thickness.

It updates the Slice node adding the following properties:
* SlabReconstructionEnabled
* SlabReconstructionType
* SlabReconstructionThickness
* SlabReconstructionOversamplingFactor

Finally, it also updates vtkMRMLSliceNodeTest adding the following tests:
* SlabReconstructionEnabledTest
* SlabReconstructionTypeTest
* SlabReconstructionThicknessTest

Co-authored-by: Jean-Christophe Fillion-Robin <jchris.fillionr@kitware.com>
  • Loading branch information
stephencrowell and jcfr committed Jul 14, 2023
1 parent 96f85fe commit 530b8fc
Show file tree
Hide file tree
Showing 12 changed files with 457 additions and 1 deletion.
90 changes: 90 additions & 0 deletions Libs/MRML/Core/Testing/vtkMRMLSliceNodeTest1.cxx
Expand Up @@ -32,6 +32,9 @@ int GetSliceOrientationPresetTest();
int GetSliceOrientationPresetNameTest();
int SetOrientationTest();
int InitializeDefaultMatrixTest();
int SlabReconstructionEnabledTest();
int SlabReconstructionTypeTest();
int SlabReconstructionThicknessTest();

//----------------------------------------------------------------------------
int vtkMRMLSliceNodeTest1(int , char * [] )
Expand All @@ -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;
}
Expand Down Expand Up @@ -426,3 +432,87 @@ int InitializeDefaultMatrixTest()

return EXIT_SUCCESS;
}

//----------------------------------------------------------------------------
int SlabReconstructionEnabledTest()
{
vtkNew<vtkMRMLSliceNode> 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<vtkMRMLSliceNode> 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<vtkMRMLSliceNode> sliceNode;

CHECK_DOUBLE(sliceNode->GetSlabReconstructionThickness(), 1.);

// Set using set macro
{
sliceNode->SetSlabReconstructionThickness(99.5);
CHECK_DOUBLE(sliceNode->GetSlabReconstructionThickness(), 99.5);
}

return EXIT_SUCCESS;
}
61 changes: 61 additions & 0 deletions Libs/MRML/Core/vtkMRMLSliceNode.cxx
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -1199,6 +1219,11 @@ void vtkMRMLSliceNode::PrintSelf(ostream& os, vtkIndent indent)

vtkMRMLPrintStringMacro(DefaultOrientation);

vtkMRMLPrintBooleanMacro(SlabReconstructionEnabled);
vtkMRMLPrintEnumMacro(SlabReconstructionType);
vtkMRMLPrintFloatMacro(SlabReconstructionThickness);
vtkMRMLPrintFloatMacro(SlabReconstructionOversamplingFactor);

vtkMRMLPrintEndMacro();
}

Expand Down Expand Up @@ -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;
}
32 changes: 32 additions & 0 deletions Libs/MRML/Core/vtkMRMLSliceNode.h
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down
40 changes: 40 additions & 0 deletions Libs/MRML/Logic/vtkMRMLSliceLogic.cxx
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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<int>(sliceNode->GetSlabReconstructionThickness() / sliceSpacing);
}
reslice->SetSlabNumberOfSlices(slabNumberOfSlices);

reslice->SetSlabMode(sliceNode->GetSlabReconstructionType());

double slabSliceSpacingFraction = sliceSpacing / sliceNode->GetSlabReconstructionOversamplingFactor();
reslice->SetSlabSliceSpacingFraction(slabSliceSpacingFraction);
}

//----------------------------------------------------------------------------
void vtkMRMLSliceLogic::UpdatePipeline()
Expand Down
3 changes: 3 additions & 0 deletions Libs/MRML/Logic/vtkMRMLSliceLogic.h
Expand Up @@ -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]);
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 530b8fc

Please sign in to comment.