diff --git a/Libs/vtkSegmentationCore/Testing/CMakeLists.txt b/Libs/vtkSegmentationCore/Testing/CMakeLists.txt index 84166f75c5f..a28a7bde87e 100644 --- a/Libs/vtkSegmentationCore/Testing/CMakeLists.txt +++ b/Libs/vtkSegmentationCore/Testing/CMakeLists.txt @@ -3,6 +3,7 @@ set(KIT vtkSegmentationCore) create_test_sourcelist(Tests ${KIT}CxxTests.cxx vtkSegmentationTest1.cxx vtkSegmentationTest2.cxx + vtkSegmentationHistoryTest1.cxx vtkSegmentationConverterTest1.cxx vtkClosedSurfaceToFractionalLabelMapConversionTest1.cxx ) @@ -13,5 +14,6 @@ set_target_properties(${KIT}CxxTests PROPERTIES FOLDER ${${PROJECT_NAME}_FOLDER} simple_test( vtkSegmentationTest1 ) simple_test( vtkSegmentationTest2 ) +simple_test( vtkSegmentationHistoryTest1 ) simple_test( vtkSegmentationConverterTest1 ) simple_test( vtkClosedSurfaceToFractionalLabelMapConversionTest1 ) diff --git a/Libs/vtkSegmentationCore/Testing/vtkSegmentationHistoryTest1.cxx b/Libs/vtkSegmentationCore/Testing/vtkSegmentationHistoryTest1.cxx new file mode 100644 index 00000000000..8ac05af8c3e --- /dev/null +++ b/Libs/vtkSegmentationCore/Testing/vtkSegmentationHistoryTest1.cxx @@ -0,0 +1,183 @@ +/*============================================================================== + + Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) + Queen's University, Kingston, ON, Canada. All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Kyle Sunderland, PerkLab, Queen's University + and was supported through CANARIE's Research Software Program, and Cancer + Care Ontario. + +==============================================================================*/ + +// VTK includes +#include +#include +#include +#include +#include +#include +#include +#include + +// SegmentationCore includes +#include "vtkBinaryLabelmapToClosedSurfaceConversionRule.h" +#include "vtkClosedSurfaceToBinaryLabelmapConversionRule.h" +#include "vtkOrientedImageData.h" +#include "vtkOrientedImageDataResample.h" +#include "vtkSegment.h" +#include "vtkSegmentation.h" +#include "vtkSegmentationConverterFactory.h" +#include "vtkSegmentationHistory.h" + + +int CreateCubeLabelmap(vtkOrientedImageData* imageData, int extent[6]); +void SetReferenceGeometry(vtkSegmentation*); + +//---------------------------------------------------------------------------- +bool StateCountCheck(vtkSegmentationHistory* history, int epectedNumberOfStates) +{ + if (history->GetNumberOfStates() != epectedNumberOfStates) + { + std::cerr << "Expected " << epectedNumberOfStates << " states, found: " << history->GetNumberOfStates(); + return false; + } + return true; +} + +//---------------------------------------------------------------------------- +int GetVoxelCount(vtkImageData* labelmap, int labelValue) +{ + vtkNew threshold; + threshold->SetInputData(labelmap); + threshold->SetInValue(1); + threshold->SetOutValue(0); + threshold->ThresholdBetween(labelValue, labelValue); + vtkNew accumulate; + accumulate->SetInputConnection(threshold->GetOutputPort()); + accumulate->IgnoreZeroOn(); + accumulate->Update(); + return accumulate->GetVoxelCount(); +} + +//---------------------------------------------------------------------------- +int vtkSegmentationHistoryTest1(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) +{ + // Register converter rules + vtkSegmentationConverterFactory::GetInstance()->RegisterConverterRule( + vtkSmartPointer::New()); + vtkSegmentationConverterFactory::GetInstance()->RegisterConverterRule( + vtkSmartPointer::New()); + + int segment1LabelValue = 1; + int segment2LabelValue = 2; + + vtkNew segment1; + segment1->SetName("Segment_1"); + segment1->SetLabelValue(segment1LabelValue); + vtkNew segment2; + segment2->SetName("Segment_2"); + segment2->SetLabelValue(segment2LabelValue); + + vtkNew labelmap; + segment1->AddRepresentation(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName(), labelmap); + segment2->AddRepresentation(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName(), labelmap); + + vtkNew segmentation; + segmentation->AddSegment(segment1); + segmentation->AddSegment(segment2); + + vtkNew history; + history->SetSegmentation(segmentation); + if (!StateCountCheck(history, 0)) + { + return EXIT_FAILURE; + } + + int segmentExtent[6] = { 0, 25, 0, 25, 0, 25 }; + CreateCubeLabelmap(labelmap, segmentExtent); + history->SaveState(); + if (!StateCountCheck(history, 1)) + { + return EXIT_FAILURE; + } + + int originalSegment1VoxelCount = GetVoxelCount(labelmap, segment1LabelValue); + int originalSegment2VoxelCount = GetVoxelCount(labelmap, segment2LabelValue); + + int modifierExtent[6] = { 5, 10, 5, 15, 15, 20 }; + vtkNew modifierLabelmap; + CreateCubeLabelmap(modifierLabelmap, modifierExtent); + history->SaveState(); + vtkOrientedImageDataResample::ModifyImage(labelmap, modifierLabelmap, vtkOrientedImageDataResample::OPERATION_MASKING, nullptr, 0.0, 2.0); + if (!StateCountCheck(history, 2)) + { + return EXIT_FAILURE; + } + + int modfiedSegment1VoxelCount = GetVoxelCount(labelmap, segment1LabelValue); + int modfiedSegment2VoxelCount = GetVoxelCount(labelmap, segment2LabelValue); + if (modfiedSegment1VoxelCount == originalSegment1VoxelCount) + { + std::cerr << "Segment 1 original voxel count (" << originalSegment2VoxelCount << ") and modified voxel count (" << modfiedSegment1VoxelCount << ") should not match!" << std::endl; + return EXIT_FAILURE; + } + + if (modfiedSegment2VoxelCount == originalSegment2VoxelCount) + { + std::cerr << "Segment 2 original voxel count (" << originalSegment2VoxelCount << ") and modified voxel count (" << modfiedSegment2VoxelCount << ") should not match!" << std::endl; + return EXIT_FAILURE; + } + + ///////////////////////////////////////////////// + // Test undo + // Voxel count should be the same as the original + ///////////////////////////////////////////////// + history->RestorePreviousState(); + vtkOrientedImageData* undoLabelmap = vtkOrientedImageData::SafeDownCast(segment1->GetRepresentation(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName())); + + int undoSegment1VoxelCount = GetVoxelCount(undoLabelmap, segment1LabelValue); + if (undoSegment1VoxelCount != originalSegment1VoxelCount) + { + std::cerr << "Segment 1 original voxel count (" << originalSegment1VoxelCount << ") and undo voxel count (" << undoSegment1VoxelCount << ") does not match!" << std::endl; + return EXIT_FAILURE; + } + + int undoSegment2VoxelCount = GetVoxelCount(undoLabelmap, segment2LabelValue); + if (undoSegment2VoxelCount != originalSegment2VoxelCount) + { + std::cerr << "Segment 2 original voxel count (" << originalSegment2VoxelCount << ") and undo voxel count (" << undoSegment2VoxelCount << ") does not match!" << std::endl; + return EXIT_FAILURE; + } + + ///////////////////////////////////////////////// + // Test redo + // Voxel count should be the same as the modified + ///////////////////////////////////////////////// + history->RestoreNextState(); + vtkOrientedImageData* redoLabelmap = vtkOrientedImageData::SafeDownCast(segment1->GetRepresentation(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName())); + int redoSegment1VoxelCount = GetVoxelCount(redoLabelmap, segment1LabelValue); + if (redoSegment1VoxelCount != modfiedSegment1VoxelCount) + { + std::cerr << "Segment 1 modified voxel count (" << modfiedSegment1VoxelCount << ") and redo voxel count (" << redoSegment1VoxelCount << ") does not match!" << std::endl; + return EXIT_FAILURE; + } + + int redoSegment2VoxelCount = GetVoxelCount(redoLabelmap, segment2LabelValue); + if (redoSegment2VoxelCount != modfiedSegment2VoxelCount) + { + std::cerr << "Segment 2 modified voxel count (" << modfiedSegment2VoxelCount << ") and redo voxel count (" << redoSegment2VoxelCount << ") does not match!" << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Segmentation history test 1 passed." << std::endl; + return EXIT_SUCCESS; +} diff --git a/Libs/vtkSegmentationCore/Testing/vtkSegmentationTest2.cxx b/Libs/vtkSegmentationCore/Testing/vtkSegmentationTest2.cxx index 8da03303da3..6c72402e106 100644 --- a/Libs/vtkSegmentationCore/Testing/vtkSegmentationTest2.cxx +++ b/Libs/vtkSegmentationCore/Testing/vtkSegmentationTest2.cxx @@ -342,6 +342,7 @@ int vtkSegmentationTest2(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) return EXIT_SUCCESS; } +//---------------------------------------------------------------------------- void SetReferenceGeometry(vtkSegmentation* segmentation) { //// Create non-default reference geometry for conversion diff --git a/Libs/vtkSegmentationCore/vtkSegmentationHistory.cxx b/Libs/vtkSegmentationCore/vtkSegmentationHistory.cxx index 29835e65159..3bccde29d53 100644 --- a/Libs/vtkSegmentationCore/vtkSegmentationHistory.cxx +++ b/Libs/vtkSegmentationCore/vtkSegmentationHistory.cxx @@ -409,3 +409,9 @@ void vtkSegmentationHistory::RemoveAllStates() this->LastRestoredState = 0; this->Modified(); } + +//--------------------------------------------------------------------------- +int vtkSegmentationHistory::GetNumberOfStates() +{ + return this->SegmentationStates.size(); +} diff --git a/Libs/vtkSegmentationCore/vtkSegmentationHistory.h b/Libs/vtkSegmentationCore/vtkSegmentationHistory.h index 58f03d570df..b3daa525072 100644 --- a/Libs/vtkSegmentationCore/vtkSegmentationHistory.h +++ b/Libs/vtkSegmentationCore/vtkSegmentationHistory.h @@ -83,6 +83,9 @@ class vtkSegmentationCore_EXPORT vtkSegmentationHistory : public vtkObject /// Get the limit of how many states may be stored. vtkGetMacro(MaximumNumberOfStates, unsigned int); + /// Get the current number of states. + int GetNumberOfStates(); + protected: /// Callback function called when the segmentation has been modified. /// It clears all states that are more recent than the last restored state.