├── CMakeLists.txt ├── LICENSE ├── README.md ├── doc └── images │ ├── AutomaticOversampling_FuzzyMemberships.png │ ├── Fig_ConversionGraph.png │ ├── Fig_ProvenanceChallenge.png │ ├── Fig_SegmentationObject.PNG │ ├── Fig_StructureSetRepresentations.png │ └── PolySegArchitecture.png └── src ├── CMakeLists.txt ├── PolySegConfig.cmake.in ├── PolySegConfigure.h.in ├── Testing ├── CMakeLists.txt ├── vtkClosedSurfaceToFractionalLabelMapConversionTest1.cxx ├── vtkSegmentationConverterTest1.cxx ├── vtkSegmentationTest1.cxx └── vtkSegmentationTest2.cxx ├── vtkBinaryLabelmapToClosedSurfaceConversionRule.cxx ├── vtkBinaryLabelmapToClosedSurfaceConversionRule.h ├── vtkCalculateOversamplingFactor.cxx ├── vtkCalculateOversamplingFactor.h ├── vtkClosedSurfaceToBinaryLabelmapConversionRule.cxx ├── vtkClosedSurfaceToBinaryLabelmapConversionRule.h ├── vtkClosedSurfaceToFractionalLabelmapConversionRule.cxx ├── vtkClosedSurfaceToFractionalLabelmapConversionRule.h ├── vtkFractionalLabelmapToClosedSurfaceConversionRule.cxx ├── vtkFractionalLabelmapToClosedSurfaceConversionRule.h ├── vtkOrientedImageData.cxx ├── vtkOrientedImageData.h ├── vtkOrientedImageDataResample.cxx ├── vtkOrientedImageDataResample.h ├── vtkPolyDataToFractionalLabelmapFilter.cxx ├── vtkPolyDataToFractionalLabelmapFilter.h ├── vtkSegment.cxx ├── vtkSegment.h ├── vtkSegmentation.cxx ├── vtkSegmentation.h ├── vtkSegmentationConverter.cxx ├── vtkSegmentationConverter.h ├── vtkSegmentationConverterFactory.cxx ├── vtkSegmentationConverterFactory.h ├── vtkSegmentationConverterRule.cxx ├── vtkSegmentationConverterRule.h ├── vtkSegmentationHistory.cxx ├── vtkSegmentationHistory.h ├── vtkTopologicalHierarchy.cxx └── vtkTopologicalHierarchy.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(PolySeg) 2 | 3 | #----------------------------------------------------------------------------- 4 | cmake_minimum_required(VERSION 3.5) 5 | 6 | # -------------------------------------------------------------------------- 7 | add_subdirectory(src) 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2018, The Perk Lab - Laboratory for Percutaneous Surgery 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PolySeg: Library for polymorph segmentation representation for medical image computing 2 | 3 | Various data structures are available to represent segmentation results. Unfortunately none of them are optimal for storage, analysis, and real-time visualization at the same time, so a trade-off is typically made to choose one that is most suitable for the main purpose of the application. 4 | 5 | 6 | 7 | There are challenges involved in the design and implementation of storage and analysis of anatomical structures that must be addressed in all software applications: 8 | * Conversion method selection: The need for conversion is needs to be recognized, and the optimal conversion method determined 9 | * Provenance: Relationships between converted objects need to be preserved to be able to determine their origin and identity (see figure below) 10 | * Consistency: When a representation changes, the others become invalid, and the data scene inconsistent. Must ensure no invalid data is accessible at any time 11 | * Coherence: Structure sets typically correspond to the same entity (i.e., a patient), so when objects are stored in memory or disk, processed, or visualized it should be possible to manage them as a unified whole 12 | 13 | 14 | 15 | ## Design of the segmentation object 16 | 17 | A _segmentation object_ contains multiple structures and representations in one object. Each structure in a segmentation object is a _segment_, which can contain multiple representations. Segments contain their basic properties such as name and color, as well as a dictionary for storing any additional metadata. 18 | 19 | 20 | 21 | The automatic conversion between the different segment representations is driven by a conversion graph. The nodes of the graph are the representations, and the directed edges are conversion algorithms; a typical graph is shown below. 22 | 23 | 24 | 25 | A simplified architecture diagram of PolySeg integrated within the [3D Slicer](www.slicer.org) open-source medical image analysis and visualization platform: 26 | 27 | ![PolySeg architecture](doc/images/PolySegArchitecture.png) 28 | 29 | ## How to cite 30 | 31 | Pinter, C., Lasso, A., & Fichtinger, G. (2019). Polymorph segmentation representation for medical image computing. Computer Methods and Programs in Biomedicine, 171, 19–26. https://doi.org/10.1016/j.cmpb.2019.02.011 32 | -------------------------------------------------------------------------------- /doc/images/AutomaticOversampling_FuzzyMemberships.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PerkLab/PolySeg/7186d8bba27b7aefc67abfa019898d8079be8b78/doc/images/AutomaticOversampling_FuzzyMemberships.png -------------------------------------------------------------------------------- /doc/images/Fig_ConversionGraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PerkLab/PolySeg/7186d8bba27b7aefc67abfa019898d8079be8b78/doc/images/Fig_ConversionGraph.png -------------------------------------------------------------------------------- /doc/images/Fig_ProvenanceChallenge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PerkLab/PolySeg/7186d8bba27b7aefc67abfa019898d8079be8b78/doc/images/Fig_ProvenanceChallenge.png -------------------------------------------------------------------------------- /doc/images/Fig_SegmentationObject.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PerkLab/PolySeg/7186d8bba27b7aefc67abfa019898d8079be8b78/doc/images/Fig_SegmentationObject.PNG -------------------------------------------------------------------------------- /doc/images/Fig_StructureSetRepresentations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PerkLab/PolySeg/7186d8bba27b7aefc67abfa019898d8079be8b78/doc/images/Fig_StructureSetRepresentations.png -------------------------------------------------------------------------------- /doc/images/PolySegArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PerkLab/PolySeg/7186d8bba27b7aefc67abfa019898d8079be8b78/doc/images/PolySegArchitecture.png -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(PolySeg) 2 | 3 | #----------------------------------------------------------------------------- 4 | cmake_minimum_required(VERSION 3.5) 5 | #----------------------------------------------------------------------------- 6 | 7 | # -------------------------------------------------------------------------- 8 | # Try to find VTK and include its settings (otherwise complain) 9 | FIND_PACKAGE(VTK NO_MODULE REQUIRED PATHS ${VTK_DIR} NO_DEFAULT_PATH) 10 | 11 | IF(VTK_FOUND) 12 | INCLUDE(${VTK_USE_FILE}) 13 | ELSE() 14 | MESSAGE(FATAL_ERROR "This application requires VTK. One of these components is missing. Please verify configuration") 15 | ENDIF() 16 | 17 | # -------------------------------------------------------------------------- 18 | # Configure headers 19 | # -------------------------------------------------------------------------- 20 | set(configure_header_file PolySegConfigure.h) 21 | configure_file( 22 | ${CMAKE_CURRENT_SOURCE_DIR}/${configure_header_file}.in 23 | ${CMAKE_CURRENT_BINARY_DIR}/${configure_header_file} 24 | ) 25 | 26 | # -------------------------------------------------------------------------- 27 | # Install headers 28 | # -------------------------------------------------------------------------- 29 | if(NOT DEFINED ${PROJECT_NAME}_INSTALL_NO_DEVELOPMENT) 30 | set(${PROJECT_NAME}_INSTALL_NO_DEVELOPMENT ON) 31 | endif() 32 | if(NOT ${PROJECT_NAME}_INSTALL_NO_DEVELOPMENT) 33 | file(GLOB headers "${CMAKE_CURRENT_SOURCE_DIR}/*.(h|txx)") 34 | install( 35 | FILES ${headers} ${CMAKE_CURRENT_BINARY_DIR}/${configure_header_file} 36 | DESTINATION include/${PROJECT_NAME} COMPONENT Development) 37 | endif() 38 | 39 | # -------------------------------------------------------------------------- 40 | # Sources 41 | # -------------------------------------------------------------------------- 42 | 43 | set(PolySeg_SRCS 44 | vtkOrientedImageData.cxx 45 | vtkOrientedImageData.h 46 | vtkOrientedImageDataResample.cxx 47 | vtkOrientedImageDataResample.h 48 | vtkSegment.cxx 49 | vtkSegment.h 50 | vtkSegmentation.cxx 51 | vtkSegmentation.h 52 | vtkSegmentationConverter.cxx 53 | vtkSegmentationConverter.h 54 | vtkSegmentationConverterFactory.cxx 55 | vtkSegmentationConverterFactory.h 56 | vtkSegmentationConverterRule.cxx 57 | vtkSegmentationConverterRule.h 58 | vtkSegmentationHistory.cxx 59 | vtkSegmentationHistory.h 60 | vtkTopologicalHierarchy.cxx 61 | vtkTopologicalHierarchy.h 62 | vtkBinaryLabelmapToClosedSurfaceConversionRule.cxx 63 | vtkBinaryLabelmapToClosedSurfaceConversionRule.h 64 | vtkClosedSurfaceToBinaryLabelmapConversionRule.cxx 65 | vtkClosedSurfaceToBinaryLabelmapConversionRule.h 66 | vtkCalculateOversamplingFactor.cxx 67 | vtkCalculateOversamplingFactor.h 68 | vtkClosedSurfaceToFractionalLabelmapConversionRule.h 69 | vtkClosedSurfaceToFractionalLabelmapConversionRule.cxx 70 | vtkFractionalLabelmapToClosedSurfaceConversionRule.h 71 | vtkFractionalLabelmapToClosedSurfaceConversionRule.cxx 72 | vtkPolyDataToFractionalLabelmapFilter.h 73 | vtkPolyDataToFractionalLabelmapFilter.cxx 74 | ) 75 | 76 | # Abstract/pure virtual classes 77 | 78 | #set_source_files_properties( 79 | # ABSTRACT 80 | # ) 81 | 82 | set(PolySeg_INCLUDE_DIRS 83 | ${CMAKE_CURRENT_SOURCE_DIR} 84 | ${CMAKE_CURRENT_BINARY_DIR} 85 | CACHE INTERNAL "" FORCE 86 | ) 87 | 88 | # -------------------------------------------------------------------------- 89 | # Build the library 90 | # -------------------------------------------------------------------------- 91 | 92 | set(PolySeg_LIBS 93 | ${VTK_LIBRARIES} 94 | ) 95 | 96 | include_directories( ${PolySeg_INCLUDE_DIRS} ) 97 | add_library(${PROJECT_NAME} ${PolySeg_SRCS}) 98 | target_link_libraries( ${PROJECT_NAME} ${PolySeg_LIBS} ) 99 | 100 | if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT WIN32) 101 | set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "-fPIC") 102 | endif() 103 | 104 | # -------------------------------------------------------------------------- 105 | # Folder 106 | # -------------------------------------------------------------------------- 107 | if(NOT DEFINED ${PROJECT_NAME}_FOLDER) 108 | set(${PROJECT_NAME}_FOLDER ${PROJECT_NAME}) 109 | endif() 110 | if(NOT "${${PROJECT_NAME}_FOLDER}" STREQUAL "") 111 | set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${${PROJECT_NAME}_FOLDER}) 112 | endif() 113 | 114 | # -------------------------------------------------------------------------- 115 | # Export target 116 | # -------------------------------------------------------------------------- 117 | if(NOT DEFINED ${PROJECT_NAME}_EXPORT_FILE) 118 | set(${PROJECT_NAME}_EXPORT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake) 119 | endif() 120 | export(TARGETS ${PROJECT_NAME} APPEND FILE ${${PROJECT_NAME}_EXPORT_FILE}) 121 | 122 | # -------------------------------------------------------------------------- 123 | # Install library 124 | # -------------------------------------------------------------------------- 125 | if(NOT DEFINED ${PROJECT_NAME}_INSTALL_BIN_DIR) 126 | set(${PROJECT_NAME}_INSTALL_BIN_DIR bin) 127 | endif() 128 | if(NOT DEFINED ${PROJECT_NAME}_INSTALL_LIB_DIR) 129 | set(${PROJECT_NAME}_INSTALL_LIB_DIR lib/${PROJECT_NAME}) 130 | endif() 131 | 132 | install(TARGETS ${PROJECT_NAME} 133 | RUNTIME DESTINATION ${${PROJECT_NAME}_INSTALL_BIN_DIR} COMPONENT RuntimeLibraries 134 | LIBRARY DESTINATION ${${PROJECT_NAME}_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries 135 | ARCHIVE DESTINATION ${${PROJECT_NAME}_INSTALL_LIB_DIR} COMPONENT Development 136 | ) 137 | 138 | # -------------------------------------------------------------------------- 139 | # Python wrapping 140 | # -------------------------------------------------------------------------- 141 | if(VTK_WRAP_PYTHON AND BUILD_SHARED_LIBS) 142 | 143 | include(vtkMacroKitPythonWrap) 144 | 145 | vtkMacroKitPythonWrap( 146 | KIT_NAME ${PROJECT_NAME} 147 | KIT_SRCS ${PolySeg_SRCS} 148 | KIT_INSTALL_BIN_DIR ${${PROJECT_NAME}_INSTALL_BIN_DIR} 149 | KIT_INSTALL_LIB_DIR ${${PROJECT_NAME}_INSTALL_LIB_DIR} 150 | ) 151 | 152 | # Export target 153 | export(TARGETS ${PROJECT_NAME}Python ${PROJECT_NAME}PythonD APPEND FILE ${${PROJECT_NAME}_EXPORT_FILE}) 154 | # Folder 155 | if(NOT "${${PROJECT_NAME}_FOLDER}" STREQUAL "") 156 | set_target_properties(${PROJECT_NAME}Python PROPERTIES FOLDER ${${PROJECT_NAME}_FOLDER}) 157 | set_target_properties(${PROJECT_NAME}PythonD PROPERTIES FOLDER ${${PROJECT_NAME}_FOLDER}) 158 | if(TARGET ${PROJECT_NAME}Hierarchy) 159 | set_target_properties(${PROJECT_NAME}Hierarchy PROPERTIES FOLDER ${${PROJECT_NAME}_FOLDER}) 160 | endif() 161 | endif() 162 | endif() 163 | 164 | # -------------------------------------------------------------------------- 165 | # Testing 166 | # -------------------------------------------------------------------------- 167 | if(BUILD_TESTING) 168 | add_subdirectory(Testing) 169 | endif() 170 | 171 | # -------------------------------------------------------------------------- 172 | # Set INCLUDE_DIRS variable 173 | # -------------------------------------------------------------------------- 174 | set(${PROJECT_NAME}_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} 175 | CACHE INTERNAL "${PROJECT_NAME} include dirs" FORCE) 176 | -------------------------------------------------------------------------------- /src/PolySegConfig.cmake.in: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------- 2 | # Configuration file for the PolySeg library 3 | # 4 | # © Copyright 2018 The Laboratory for Percutaneous Surgery, Queen's University, Canada 5 | # 6 | # This file can be passed to a CMake FIND_PACKAGE call with the following syntax: 7 | # 8 | # FIND_PACKAGE(PolySeg @PolySeg_VERSION@ ) 9 | # If NO_MODULE is included, set the variable PolySeg_DIR:PATH=@CMAKE_BINARY_DIR@ 10 | # 11 | # Once successful, you can either use the USE_FILE approach by the following CMake command: 12 | # INCLUDE(${PLUS_USE_FILE}) 13 | # 14 | # Or you can use the following variables to configure your CMake project: 15 | # PolySeg_INCLUDE_DIRS - include directories for Plus headers 16 | # PolySeg_LIBRARIES - list of CMake targets produced by this build of Plus 17 | # PolySeg_DATA_DIR - directory containing data collector configuration files, sample images, and 3d models 18 | #----------------------------------------------------------------------------- 19 | 20 | # set the targets file (relative for install location) 21 | @PolySeg_CONFIG_CODE@ 22 | IF(VTK_FOUND) 23 | IF(NOT ${VTK_VERSION} VERSION_EQUAL "@VTK_VERSION@") 24 | MESSAGE(SEND_ERROR "Trying to use different VTK versions in PolySeg (@VTK_VERSION@) and your superceeding project (${VTK_VERSION}), use the same version of VTK.") 25 | ENDIF() 26 | ELSE() 27 | SET(VTK_DIR "@VTK_CONFIG_DIR@" CACHE PATH "Path to VTK") 28 | ENDIF() 29 | 30 | FIND_PACKAGE(VTK "@VTK_VERSION@" EXACT REQUIRED NO_MODULE) 31 | INCLUDE(${VTK_USE_FILE}) # Must be included as VTK does not populate CMake target properties correctly 32 | 33 | INCLUDE(${PolySeg_TARGETS_FILE}) 34 | 35 | # Tell the user project where to find our headers and libraries 36 | SET(PolySeg_INCLUDE_DIRS "@PolySeg_INCLUDE_DIRS@") 37 | SET(PolySeg_DATA_DIR "@PolySeg_DATA_DIR@") 38 | SET(PolySeg_LIBRARIES "@PolySeg_DEPENDENCIES@") 39 | 40 | # Tell the user project where to find Plus use file 41 | SET(PolySeg_USE_FILE "@PolySeg_CONFIG_USEFILE@") 42 | -------------------------------------------------------------------------------- /src/PolySegConfigure.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * Here is where system computed values get stored. 3 | * These values should only change when the target compile platform changes. 4 | */ 5 | 6 | #cmakedefine BUILD_SHARED_LIBS 7 | #ifndef BUILD_SHARED_LIBS 8 | #define PolySeg_STATIC 9 | #endif 10 | 11 | #if defined(WIN32) && !defined(PolySeg_STATIC) 12 | #pragma warning ( disable : 4275 ) 13 | 14 | #if defined(PolySeg_EXPORT) 15 | #define PolySeg_EXPORT __declspec( dllexport ) 16 | #else 17 | #define PolySeg_EXPORT __declspec( dllimport ) 18 | #endif 19 | #else 20 | #define PolySeg_EXPORT 21 | #endif 22 | -------------------------------------------------------------------------------- /src/Testing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(KIT PolySeg) 2 | 3 | create_test_sourcelist(Tests ${KIT}CxxTests.cxx 4 | vtkSegmentationTest1.cxx 5 | vtkSegmentationTest2.cxx 6 | vtkSegmentationConverterTest1.cxx 7 | vtkClosedSurfaceToFractionalLabelMapConversionTest1.cxx 8 | ) 9 | 10 | add_executable(${KIT}CxxTests ${Tests}) 11 | target_link_libraries(${KIT}CxxTests ${PROJECT_NAME}) 12 | set_target_properties(${KIT}CxxTests PROPERTIES FOLDER ${${PROJECT_NAME}_FOLDER}) 13 | 14 | macro(simple_test TEST_NAME) 15 | add_test(${TEST_NAME} 16 | ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${KIT}CxxTests 17 | ${TEST_NAME} 18 | ) 19 | endmacro() 20 | 21 | simple_test( vtkSegmentationTest1 ) 22 | simple_test( vtkSegmentationTest2 ) 23 | simple_test( vtkSegmentationConverterTest1 ) 24 | simple_test( vtkClosedSurfaceToFractionalLabelMapConversionTest1 ) 25 | -------------------------------------------------------------------------------- /src/Testing/vtkClosedSurfaceToFractionalLabelMapConversionTest1.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See COPYRIGHT.txt 7 | or http://www.slicer.org/copyright/copyright.txt for details. 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | This file was originally developed by Kyle Sunderland, PerkLab, Queen's University 16 | and was supported through the Applied Cancer Research Unit program of Cancer Care 17 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 18 | 19 | ==============================================================================*/ 20 | 21 | // VTK includes 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // vtkSegmentationCore includes 28 | #include "vtkClosedSurfaceToFractionalLabelmapConversionRule.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | void CreateSpherePolyData(vtkPolyData* polyData); 37 | 38 | //---------------------------------------------------------------------------- 39 | int vtkClosedSurfaceToFractionalLabelMapConversionTest1(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) 40 | { 41 | // Register converter rules 42 | vtkSegmentationConverterFactory::GetInstance()->RegisterConverterRule( 43 | vtkSmartPointer::New() ); 44 | 45 | // Generate sphere model 46 | vtkNew spherePolyData; 47 | CreateSpherePolyData(spherePolyData.GetPointer()); 48 | 49 | // Create segment 50 | vtkNew sphereSegment; 51 | sphereSegment->SetName("sphere1"); 52 | sphereSegment->AddRepresentation( 53 | vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(), spherePolyData.GetPointer()); 54 | 55 | // Image geometry used for conversion 56 | std::string serializedImageGeometry = "1; 0; 0; 20.7521629333496;" 57 | "0; 1; 0; 20.7521629333496;" 58 | "0; 0; 1; 20;" 59 | "0; 0; 0; 1;" 60 | "0; 59; 0; 59; 0; 60;"; 61 | 62 | // Create segmentation with segment 63 | vtkNew sphereSegmentation; 64 | sphereSegmentation->SetConversionParameter(vtkSegmentationConverter::GetReferenceImageGeometryParameterName(), serializedImageGeometry); 65 | sphereSegmentation->SetMasterRepresentationName( 66 | vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName()); 67 | sphereSegmentation->AddSegment(sphereSegment.GetPointer()); 68 | 69 | sphereSegmentation->CreateRepresentation(vtkSegmentationConverter::GetSegmentationFractionalLabelmapRepresentationName()); 70 | if (!sphereSegment->GetRepresentation(vtkSegmentationConverter::GetSegmentationFractionalLabelmapRepresentationName())) 71 | { 72 | std::cerr << __LINE__ << ": Failed to add fractional labelmap representation to segment!" << std::endl; 73 | return EXIT_FAILURE; 74 | } 75 | 76 | vtkOrientedImageData* fractionalLabelmap = vtkOrientedImageData::SafeDownCast( 77 | sphereSegment->GetRepresentation(vtkSegmentationConverter::GetSegmentationFractionalLabelmapRepresentationName()) ); 78 | 79 | vtkNew imageAccumulate; 80 | imageAccumulate->SetInputData(fractionalLabelmap); 81 | imageAccumulate->Update(); 82 | 83 | int expectedVoxelCount = 234484; 84 | int voxelCount = imageAccumulate->GetVoxelCount(); 85 | if (voxelCount != expectedVoxelCount) 86 | { 87 | std::cerr << __LINE__ << ": Fractional voxel count: " << +voxelCount << 88 | " does not match expected value: " << std::fixed << +expectedVoxelCount << "!" << std::endl; 89 | return EXIT_FAILURE; 90 | } 91 | 92 | FRACTIONAL_DATA_TYPE maxValue = imageAccumulate->GetMax()[0]; 93 | int expectedMaxValue = FRACTIONAL_MAX; 94 | if (maxValue != expectedMaxValue) 95 | { 96 | std::cerr << __LINE__ << ": Fractional max: " << +maxValue << 97 | " does not match expected value: " << std::fixed << +expectedMaxValue << "!" << std::endl; 98 | return EXIT_FAILURE; 99 | } 100 | 101 | FRACTIONAL_DATA_TYPE minValue = imageAccumulate->GetMin()[0]; 102 | int expectedMinValue = FRACTIONAL_MIN; 103 | if (minValue != expectedMinValue) 104 | { 105 | std::cerr << __LINE__ << ": Fractional min: " << +minValue << 106 | " does not match expected value: " << std::fixed << +expectedMinValue << "!" << std::endl; 107 | return EXIT_FAILURE; 108 | } 109 | 110 | double expectedMeanValue = 0; 111 | if (VTK_FRACTIONAL_DATA_TYPE == VTK_CHAR) 112 | { 113 | // Average signed value 114 | expectedMeanValue = -18.846224; 115 | } 116 | else if (VTK_FRACTIONAL_DATA_TYPE == VTK_UNSIGNED_CHAR) 117 | { 118 | // Average unsigned value 119 | expectedMeanValue = 89.153776; 120 | } 121 | else 122 | { 123 | std::cerr << __LINE__ << ": Fractional datatype: " << std::fixed << VTK_FRACTIONAL_DATA_TYPE << 124 | " is not a supported datatype:!" << std::endl; 125 | return EXIT_FAILURE; 126 | } 127 | double meanValue = imageAccumulate->GetMean()[0]; 128 | if ( std::abs(meanValue - expectedMeanValue) > 0.00001) 129 | { 130 | std::cerr << __LINE__ << ": Fractional mean: " << std::fixed << meanValue << 131 | " does not match expected value: " << std::fixed << expectedMeanValue << "!" << std::endl; 132 | return EXIT_FAILURE; 133 | } 134 | 135 | std::cout << "Closed surface to fractional labelmap conversion test passed." << std::endl; 136 | return EXIT_SUCCESS; 137 | } 138 | -------------------------------------------------------------------------------- /src/Testing/vtkSegmentationConverterTest1.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Andras Lasso, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | // VTK includes 21 | #include 22 | #include 23 | 24 | // SegmentationCore includes 25 | #include 26 | #include 27 | #include 28 | 29 | //---------------------------------------------------------------------------- 30 | // Test macros 31 | #define VERIFY_EQUAL(description, actual, expected) \ 32 | { \ 33 | if (expected != actual) \ 34 | { \ 35 | std::cerr << "Test failure: Mismatch in " << description << ". Expected " << expected << ", actual value is " << actual << std::endl << std::endl; \ 36 | exit(EXIT_FAILURE); \ 37 | } \ 38 | else \ 39 | { \ 40 | std::cout << "Test case success: " << description << " matches expected value " << actual << std::endl; \ 41 | } \ 42 | } 43 | 44 | //---------------------------------------------------------------------------- 45 | // Conversion graph 46 | 47 | // Convenience macro for defining a converter rule class with a single line 48 | #define RULE(from, to, weight) \ 49 | class vtkRep##from##ToRep##to##Rule: public vtkSegmentationConverterRule \ 50 | { \ 51 | public: \ 52 | static vtkRep##from##ToRep##to##Rule* New(); \ 53 | vtkTypeMacro(vtkRep##from##ToRep##to##Rule, vtkSegmentationConverterRule); \ 54 | virtual vtkSegmentationConverterRule* CreateRuleInstance() override; \ 55 | virtual vtkDataObject* ConstructRepresentationObjectByRepresentation( \ 56 | std::string vtkNotUsed(representationName)) override { return nullptr; }; \ 57 | virtual vtkDataObject* ConstructRepresentationObjectByClass( \ 58 | std::string vtkNotUsed(className)) override { return nullptr; }; \ 59 | virtual bool Convert( \ 60 | vtkSegment* vtkNotUsed(segment)) override { return true; } \ 61 | virtual unsigned int GetConversionCost( \ 62 | vtkDataObject* sourceRepresentation=nullptr, \ 63 | vtkDataObject* targetRepresentation=nullptr) override \ 64 | { \ 65 | (void)sourceRepresentation; \ 66 | (void)targetRepresentation; \ 67 | return weight; \ 68 | }; \ 69 | virtual const char* GetName() override { return "Rep " #from " to Rep " #to; } \ 70 | virtual const char* GetSourceRepresentationName() override { return "Rep" #from ; } \ 71 | virtual const char* GetTargetRepresentationName() override { return "Rep" #to ; } \ 72 | }; \ 73 | vtkSegmentationConverterRuleNewMacro(vtkRep##from##ToRep##to##Rule); 74 | 75 | /* 76 | Test conversion graph: 77 | A, B, ... = vertex = data representation type 78 | --4>-- = edge (with weight of 4) = converter rule 79 | 80 | 2>--B-1>--C 81 | / / / \ 82 | A--<3 -<2 4>--E 83 | \ / / \ 84 | 6>--D---2>----- \ 85 | \ \ 86 | -------<1------ 87 | 88 | */ 89 | // Converter rules corresponding to the test conversion graph above 90 | RULE(A, B, 2); 91 | RULE(A, D, 6); 92 | RULE(B, A, 3); 93 | RULE(B, C, 1); 94 | RULE(C, D, 2); 95 | RULE(C, E, 4); 96 | RULE(D, E, 2); 97 | RULE(E, D, 1); 98 | 99 | void PrintPath(const vtkSegmentationConverter::ConversionPathType& path) 100 | { 101 | for (vtkSegmentationConverter::ConversionPathType::const_iterator ruleIt = path.begin(); ruleIt != path.end(); ++ruleIt) 102 | { 103 | std::cout << " " << (*ruleIt)->GetName() << "(" << (*ruleIt)->GetConversionCost() << ")" << std::endl; 104 | } 105 | } 106 | 107 | //---------------------------------------------------------------------------- 108 | void TestRegisterUnregister() 109 | { 110 | vtkSegmentationConverterFactory* converterFactory = vtkSegmentationConverterFactory::GetInstance(); 111 | 112 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 113 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 114 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 115 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 116 | VERIFY_EQUAL("number of rules after register", converterFactory->GetConverterRules().size(), 4); 117 | 118 | // Remove one 119 | converterFactory->UnregisterConverterRule(converterFactory->GetConverterRules()[3]); 120 | VERIFY_EQUAL("number of rules after unregister", converterFactory->GetConverterRules().size(), 3); 121 | 122 | // Remove all 123 | while (converterFactory->GetConverterRules().size()>0) 124 | { 125 | converterFactory->UnregisterConverterRule(converterFactory->GetConverterRules()[0]); 126 | } 127 | VERIFY_EQUAL("number of rules after unregister", converterFactory->GetConverterRules().size(), 0); 128 | } 129 | 130 | //---------------------------------------------------------------------------- 131 | int vtkSegmentationConverterTest1(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) 132 | { 133 | TestRegisterUnregister(); 134 | 135 | vtkSegmentationConverterFactory* converterFactory = vtkSegmentationConverterFactory::GetInstance(); 136 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 137 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 138 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 139 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 140 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 141 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 142 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 143 | converterFactory->RegisterConverterRule(vtkSmartPointer::New()); 144 | 145 | vtkSmartPointer converter = vtkSmartPointer::New(); 146 | 147 | vtkSegmentationConverter::ConversionPathAndCostListType pathsCosts; 148 | vtkSegmentationConverter::ConversionPathType shortestPath; 149 | 150 | // A->E paths: ABCE, ABCDE, ADE 151 | std::cout << "Conversion from RepA to RepE" << std::endl; 152 | std::cout << " All paths:" << std::endl; 153 | converter->GetPossibleConversions("RepA", "RepE", pathsCosts); 154 | for (vtkSegmentationConverter::ConversionPathAndCostListType::iterator pathsCostsIt = pathsCosts.begin(); pathsCostsIt != pathsCosts.end(); ++pathsCostsIt) 155 | { 156 | std::cout << " Path: (total cost = " << pathsCostsIt->second << ")" << std::endl; 157 | PrintPath(pathsCostsIt->first); 158 | } 159 | VERIFY_EQUAL("number of paths from representation A to E", pathsCosts.size(), 3); 160 | std::cout << " Cheapest path:" << std::endl; 161 | shortestPath = vtkSegmentationConverter::GetCheapestPath(pathsCosts); 162 | PrintPath(shortestPath); 163 | VERIFY_EQUAL("number of paths from representation A to E", shortestPath.size(), 3); 164 | 165 | // E->A paths: none 166 | std::cout << "Conversion from RepE to RepA" << std::endl; 167 | converter->GetPossibleConversions("RepE", "RepA", pathsCosts); 168 | VERIFY_EQUAL("number of paths from representation E to A", pathsCosts.size(), 0); 169 | 170 | // B->D paths: BAD, BCD, BCED 171 | std::cout << "Conversion from RepB to RepD" << std::endl; 172 | std::cout << " All paths:" << std::endl; 173 | converter->GetPossibleConversions("RepB", "RepD", pathsCosts); 174 | for (vtkSegmentationConverter::ConversionPathAndCostListType::iterator pathsCostsIt = pathsCosts.begin(); pathsCostsIt != pathsCosts.end(); ++pathsCostsIt) 175 | { 176 | std::cout << " Path: (total cost = " << pathsCostsIt->second << ")" << std::endl; 177 | PrintPath(pathsCostsIt->first); 178 | } 179 | VERIFY_EQUAL("number of paths from representation B to D", pathsCosts.size(), 3); 180 | std::cout << " Cheapest path:" << std::endl; 181 | shortestPath = vtkSegmentationConverter::GetCheapestPath(pathsCosts); 182 | PrintPath(shortestPath); 183 | VERIFY_EQUAL("number of paths from representation B to D", shortestPath.size(), 2); 184 | 185 | // C->D paths: CD, CED 186 | std::cout << "Conversion from RepC to RepD" << std::endl; 187 | std::cout << " All paths:" << std::endl; 188 | converter->GetPossibleConversions("RepC", "RepD", pathsCosts); 189 | for (vtkSegmentationConverter::ConversionPathAndCostListType::iterator pathsCostsIt = pathsCosts.begin(); pathsCostsIt != pathsCosts.end(); ++pathsCostsIt) 190 | { 191 | std::cout << " Path: (total cost = " << pathsCostsIt->second << ")" << std::endl; 192 | PrintPath(pathsCostsIt->first); 193 | } 194 | VERIFY_EQUAL("number of paths from representation C to D", pathsCosts.size(), 2); 195 | std::cout << " Cheapest path:" << std::endl; 196 | shortestPath = vtkSegmentationConverter::GetCheapestPath(pathsCosts); 197 | PrintPath(shortestPath); 198 | VERIFY_EQUAL("number of paths from representation C to D", shortestPath.size(), 1); 199 | 200 | std::cout << "Test passed." << std::endl; 201 | return EXIT_SUCCESS; 202 | } 203 | -------------------------------------------------------------------------------- /src/Testing/vtkSegmentationTest1.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | // VTK includes 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | // SegmentationCore includes 29 | #include "vtkSegmentation.h" 30 | #include "vtkSegment.h" 31 | #include "vtkSegmentationConverter.h" 32 | #include "vtkOrientedImageData.h" 33 | #include "vtkSegmentationConverterFactory.h" 34 | #include "vtkBinaryLabelmapToClosedSurfaceConversionRule.h" 35 | #include "vtkClosedSurfaceToBinaryLabelmapConversionRule.h" 36 | 37 | void CreateSpherePolyData(vtkPolyData* polyData); 38 | void CreateCubeLabelmap(vtkOrientedImageData* imageData); 39 | 40 | //---------------------------------------------------------------------------- 41 | int vtkSegmentationTest1(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) 42 | { 43 | // Register converter rules 44 | vtkSegmentationConverterFactory::GetInstance()->RegisterConverterRule( 45 | vtkSmartPointer::New() ); 46 | vtkSegmentationConverterFactory::GetInstance()->RegisterConverterRule( 47 | vtkSmartPointer::New() ); 48 | 49 | ////////////////////////////////////////////////////////////////////////// 50 | // Create segmentation with one segment from model and test segment 51 | // operations and conversion to labelmap with and without reference geometry 52 | 53 | // Generate sphere model 54 | vtkNew spherePolyData; 55 | CreateSpherePolyData(spherePolyData.GetPointer()); 56 | 57 | // Create segment 58 | vtkNew sphereSegment; 59 | sphereSegment->SetName("sphere1"); 60 | sphereSegment->AddRepresentation( 61 | vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(), spherePolyData.GetPointer()); 62 | if (!sphereSegment->GetRepresentation(vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName())) 63 | { 64 | std::cerr << __LINE__ << ": Failed to add closed surface representation to segment!" << std::endl; 65 | return EXIT_FAILURE; 66 | } 67 | 68 | // Create segmentation with segment 69 | vtkNew sphereSegmentation; 70 | sphereSegmentation->SetMasterRepresentationName( 71 | vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName() ); 72 | sphereSegmentation->AddSegment(sphereSegment.GetPointer()); 73 | if (sphereSegmentation->GetNumberOfSegments() != 1) 74 | { 75 | std::cerr << __LINE__ << ": Failed to add segment to segmentation!" << std::endl; 76 | return EXIT_FAILURE; 77 | } 78 | 79 | // Convert to binary labelmap without reference geometry 80 | sphereSegmentation->CreateRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName()); 81 | vtkOrientedImageData* defaultImageData = vtkOrientedImageData::SafeDownCast( 82 | sphereSegment->GetRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName()) ); 83 | if (!defaultImageData) 84 | { 85 | std::cerr << __LINE__ << ": Failed to convert closed surface representation to binary labelmap without reference geometry!" << std::endl; 86 | return EXIT_FAILURE; 87 | } 88 | std::string defaultGeometryString = vtkSegmentationConverter::SerializeImageGeometry(defaultImageData); 89 | std::string expectedDefaultGeometryString = 90 | "0.235971522108411;0;0;20.7521629333496;" 91 | "0;0.235971522108411;0;20.7521629333496;" 92 | "0;0;0.235971522108411;20;" 93 | "0;0;0;1;" 94 | "0;248;0;248;0;255;"; 95 | if (defaultGeometryString != expectedDefaultGeometryString) 96 | { 97 | std::cerr << __LINE__ << ": Default reference geometry mismatch. Expected: " 98 | << expectedDefaultGeometryString << ". Actual: " << defaultGeometryString << "." << std::endl; 99 | return EXIT_FAILURE; 100 | } 101 | vtkNew imageAccumulate; 102 | imageAccumulate->SetInputData(defaultImageData); 103 | imageAccumulate->Update(); 104 | if (imageAccumulate->GetMax()[0] != 1) 105 | { 106 | std::cerr << __LINE__ << ": Binary labelmap converted without reference geometry has no foreground voxels!" << std::endl; 107 | return EXIT_FAILURE; 108 | } 109 | int expectedVoxelCount = 15872256; 110 | if (imageAccumulate->GetVoxelCount() != expectedVoxelCount) 111 | { 112 | std::cerr << __LINE__ << ": Binary labelmap voxel count mismatch after converting without reference geometry." 113 | << " Expected: " << expectedVoxelCount << ". Actual: << " << imageAccumulate->GetVoxelCount() << "." << std::endl; 114 | return EXIT_FAILURE; 115 | } 116 | 117 | // Remove binary labelmap representation from segment 118 | sphereSegment->RemoveRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName()); 119 | if (sphereSegment->GetRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName())) 120 | { 121 | std::cerr << __LINE__ << ": Failed to remove binary labelmap representation from segment!" << std::endl; 122 | return EXIT_FAILURE; 123 | } 124 | 125 | // Create non-default reference geometry for conversion 126 | vtkNew referenceGeometryMatrix; 127 | referenceGeometryMatrix->Identity(); 128 | referenceGeometryMatrix->SetElement(0,0,2.0); 129 | referenceGeometryMatrix->SetElement(1,1,2.0); 130 | referenceGeometryMatrix->SetElement(2,2,2.0); 131 | int referenceGeometryExtent[6] = {0,99,0,99,0,99}; 132 | std::string referenceGeometryString = vtkSegmentationConverter::SerializeImageGeometry(referenceGeometryMatrix.GetPointer(), referenceGeometryExtent); 133 | std::string expectedReferenceGeometryString = "2;0;0;0;0;2;0;0;0;0;2;0;0;0;0;1;0;99;0;99;0;99;"; 134 | if (referenceGeometryString != expectedReferenceGeometryString) 135 | { 136 | std::cerr << __LINE__ << ": Failed to serialize reference geometry. Expected: " 137 | << expectedReferenceGeometryString << ". Actual: " << referenceGeometryString << "." << std::endl; 138 | return EXIT_FAILURE; 139 | } 140 | sphereSegmentation->SetConversionParameter( 141 | vtkSegmentationConverter::GetReferenceImageGeometryParameterName(), referenceGeometryString ); 142 | 143 | // Convert to binary labelmap with reference geometry 144 | sphereSegmentation->CreateRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName()); 145 | vtkOrientedImageData* customImageData = vtkOrientedImageData::SafeDownCast( 146 | sphereSegment->GetRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName()) ); 147 | if (!customImageData) 148 | { 149 | std::cerr << __LINE__ << ": Failed to convert closed surface representation to binary labelmap with custom reference geometry!" << std::endl; 150 | return EXIT_FAILURE; 151 | } 152 | imageAccumulate->SetInputData(customImageData); 153 | imageAccumulate->Update(); 154 | if (imageAccumulate->GetMax()[0] != 1) 155 | { 156 | std::cerr << __LINE__ << ": Binary labelmap converted with custom reference geometry has no foreground voxels!" << std::endl; 157 | return EXIT_FAILURE; 158 | } 159 | if (imageAccumulate->GetVoxelCount() != 29791) 160 | { 161 | std::cerr << __LINE__ << ": Unexpected binary labelmap extent after converting with custom reference geometry!" << std::endl; 162 | return EXIT_FAILURE; 163 | } 164 | 165 | // Add second segment 166 | vtkNew spherePolyData2; 167 | CreateSpherePolyData(spherePolyData2.GetPointer()); 168 | vtkNew sphereSegment2; 169 | sphereSegment2->SetName("sphere2"); 170 | sphereSegment2->AddRepresentation( 171 | vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(), spherePolyData2.GetPointer()); 172 | if (!sphereSegment2->GetRepresentation(vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName())) 173 | { 174 | std::cerr << __LINE__ << ": Failed to add closed surface representation to second segment!" << std::endl; 175 | return EXIT_FAILURE; 176 | } 177 | sphereSegmentation->AddSegment(sphereSegment2.GetPointer()); 178 | if (sphereSegmentation->GetNumberOfSegments() != 2) 179 | { 180 | std::cerr << __LINE__ << ": Failed to add second segment to segmentation!" << std::endl; 181 | return EXIT_FAILURE; 182 | } 183 | if (!sphereSegment2->GetRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName())) 184 | { 185 | std::cerr << __LINE__ << ": Failed to auto-convert second segment to binary labelmap on adding it to segmentation!" << std::endl; 186 | return EXIT_FAILURE; 187 | } 188 | 189 | // Remove segment 190 | sphereSegmentation->RemoveSegment(sphereSegment2.GetPointer()); 191 | if (sphereSegmentation->GetNumberOfSegments() != 1) 192 | { 193 | std::cerr << __LINE__ << ": Failed to remove second segment from segmentation!" << std::endl; 194 | return EXIT_FAILURE; 195 | } 196 | 197 | // Re-add segment 198 | sphereSegmentation->AddSegment(sphereSegment2.GetPointer()); 199 | if (sphereSegmentation->GetNumberOfSegments() != 2) 200 | { 201 | std::cerr << __LINE__ << ": Failed to re-add second segment to segmentation!" << std::endl; 202 | return EXIT_FAILURE; 203 | } 204 | 205 | // Try to add segment with unsupported representation 206 | vtkNew unsupportedPolyData; 207 | CreateSpherePolyData(unsupportedPolyData.GetPointer()); 208 | vtkNew unsupportedSegment; 209 | unsupportedSegment->SetName("unsupported"); 210 | unsupportedSegment->AddRepresentation("Unsupported", unsupportedPolyData.GetPointer()); 211 | sphereSegmentation->AddSegment(unsupportedSegment.GetPointer()); 212 | if (sphereSegmentation->GetNumberOfSegments() != 2) 213 | { 214 | std::cerr << __LINE__ << ": Unexpected outcome when adding segment containing unsupported representation to segmentation!" << std::endl; 215 | return EXIT_FAILURE; 216 | } 217 | 218 | ////////////////////////////////////////////////////////////////////////// 219 | // Create segmentation with one segment from labelmap and test conversion 220 | // to closed surface model 221 | 222 | // Generate cube image data 223 | vtkNew cubeImageData; 224 | CreateCubeLabelmap(cubeImageData.GetPointer()); 225 | 226 | // Create segment 227 | vtkNew cubeSegment; 228 | cubeSegment->SetName("cube"); 229 | cubeSegment->AddRepresentation( 230 | vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName(), cubeImageData.GetPointer()); 231 | if (!cubeSegment->GetRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName())) 232 | { 233 | std::cerr << __LINE__ << ": Failed to add binary labelmap representation to segment!" << std::endl; 234 | return EXIT_FAILURE; 235 | } 236 | 237 | // Create segmentation with segment 238 | vtkNew cubeSegmentation; 239 | cubeSegmentation->SetMasterRepresentationName( 240 | vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName() ); 241 | cubeSegmentation->AddSegment(cubeSegment.GetPointer()); 242 | if (cubeSegmentation->GetNumberOfSegments() != 1) 243 | { 244 | std::cerr << __LINE__ << ": Failed to add segment to second segmentation!" << std::endl; 245 | return EXIT_FAILURE; 246 | } 247 | 248 | // Convert to closed surface model 249 | cubeSegmentation->CreateRepresentation(vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName()); 250 | vtkPolyData* closedSurfaceModel = vtkPolyData::SafeDownCast( 251 | cubeSegment->GetRepresentation(vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName()) ); 252 | if (!closedSurfaceModel) 253 | { 254 | std::cerr << __LINE__ << ": Failed to convert binary labelmap representation to closed surface model!" << std::endl; 255 | return EXIT_FAILURE; 256 | } 257 | 258 | // Add segment with closed surface representation, see if it is converted to master 259 | vtkNew nonMasterPolyData; 260 | CreateSpherePolyData(nonMasterPolyData.GetPointer()); 261 | vtkNew nonMasterSegment; 262 | nonMasterSegment->SetName("non master"); 263 | nonMasterSegment->AddRepresentation( 264 | vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(), nonMasterPolyData.GetPointer() ); 265 | cubeSegmentation->AddSegment(nonMasterSegment.GetPointer()); 266 | if (cubeSegmentation->GetNumberOfSegments() != 2) 267 | { 268 | std::cerr << __LINE__ << ": Failed to add segment with non-master representation to segmentation!" << std::endl; 269 | return EXIT_FAILURE; 270 | } 271 | if (!nonMasterSegment->GetRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName())) 272 | { 273 | std::cerr << __LINE__ << ": Master representation was not created when adding non-master segment to segmentation!" << std::endl; 274 | return EXIT_FAILURE; 275 | } 276 | 277 | ////////////////////////////////////////////////////////////////////////// 278 | // Copy and move segments between segmentations 279 | 280 | // Copy 281 | cubeSegmentation->CopySegmentFromSegmentation(sphereSegmentation.GetPointer(), "sphere1"); 282 | if (sphereSegmentation->GetNumberOfSegments() != 2 || cubeSegmentation->GetNumberOfSegments() != 3) 283 | { 284 | std::cerr << __LINE__ << ": Error when copying segment from one segmentation to another!" << std::endl; 285 | return EXIT_FAILURE; 286 | } 287 | 288 | // Move 289 | sphereSegmentation->CopySegmentFromSegmentation(cubeSegmentation.GetPointer(), "cube", true); 290 | if (sphereSegmentation->GetNumberOfSegments() != 3 || cubeSegmentation->GetNumberOfSegments() != 2) 291 | { 292 | std::cerr << __LINE__ << ": Error when moving segment from one segmentation to another!" << std::endl; 293 | return EXIT_FAILURE; 294 | } 295 | 296 | std::cout << "Segmentation test passed." << std::endl; 297 | return EXIT_SUCCESS; 298 | } 299 | 300 | //---------------------------------------------------------------------------- 301 | void CreateSpherePolyData(vtkPolyData* polyData) 302 | { 303 | if (!polyData) 304 | { 305 | return; 306 | } 307 | 308 | vtkNew sphere; 309 | sphere->SetCenter(50,50,50); 310 | sphere->SetRadius(30); 311 | sphere->Update(); 312 | polyData->DeepCopy(sphere->GetOutput()); 313 | } 314 | 315 | //---------------------------------------------------------------------------- 316 | void CreateCubeLabelmap(vtkOrientedImageData* imageData) 317 | { 318 | if (!imageData) 319 | { 320 | return; 321 | } 322 | 323 | unsigned int size = 100; 324 | 325 | // Create new one because by default the direction is identity, origin is zeros and spacing is ones 326 | vtkNew identityImageData; 327 | identityImageData->SetExtent(0,size-1,0,size,0,size-1); 328 | identityImageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1); 329 | 330 | unsigned char* imagePtr = (unsigned char*)identityImageData->GetScalarPointer(); 331 | for (unsigned int x=0; x100/4 && x100/4 && y100/4 && zDeepCopy(identityImageData.GetPointer()); 351 | } 352 | -------------------------------------------------------------------------------- /src/Testing/vtkSegmentationTest2.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See COPYRIGHT.txt 7 | or http://www.slicer.org/copyright/copyright.txt for details. 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | This file was originally developed by Kyle Sunderland, PerkLab, Queen's University 16 | and was supported through CANARIE's Research Software Program, and Cancer 17 | Care Ontario. 18 | 19 | ==============================================================================*/ 20 | 21 | // VTK includes 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | // SegmentationCore includes 31 | #include "vtkSegmentation.h" 32 | #include "vtkSegment.h" 33 | #include "vtkSegmentationConverter.h" 34 | #include "vtkOrientedImageData.h" 35 | #include "vtkOrientedImageDataResample.h" 36 | #include "vtkSegmentationConverterFactory.h" 37 | #include "vtkBinaryLabelmapToClosedSurfaceConversionRule.h" 38 | #include "vtkClosedSurfaceToBinaryLabelmapConversionRule.h" 39 | 40 | void CreateSpherePolyData(vtkPolyData* polyData, double center[3], double radius); 41 | int CreateCubeLabelmap(vtkOrientedImageData* imageData, int extent[6]); 42 | 43 | void SetReferenceGeometry(vtkSegmentation*); 44 | 45 | bool TestSharedLabelmapConversion() 46 | { 47 | // Generate sphere models 48 | vtkNew spherePolyData1; 49 | double sphereCenter1[3] = { 0,0,0 }; 50 | double sphereRadius1 = 1; 51 | CreateSpherePolyData(spherePolyData1.GetPointer(), sphereCenter1, sphereRadius1); 52 | 53 | vtkNew spherePolyData2; 54 | double sphereCenter2[3] = { -1,-1,-1 }; 55 | double sphereRadius2 = 2; 56 | CreateSpherePolyData(spherePolyData2.GetPointer(), sphereCenter2, sphereRadius2); 57 | 58 | vtkNew spherePolyData3; 59 | double sphereCenter3[3] = { 5, 5,5 }; 60 | double sphereRadius3 = 2; 61 | CreateSpherePolyData(spherePolyData3.GetPointer(), sphereCenter3, sphereRadius3); 62 | 63 | // Create segments 64 | vtkNew sphereSegment1; 65 | sphereSegment1->SetName("sphere1"); 66 | sphereSegment1->AddRepresentation( 67 | vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(), spherePolyData1.GetPointer()); 68 | 69 | vtkNew sphereSegment2; 70 | sphereSegment2->SetName("sphere2"); 71 | sphereSegment2->AddRepresentation( 72 | vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(), spherePolyData2.GetPointer()); 73 | 74 | vtkNew sphereSegment3; 75 | sphereSegment3->SetName("sphere3"); 76 | sphereSegment3->AddRepresentation( 77 | vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(), spherePolyData3.GetPointer()); 78 | 79 | vtkNew segmentation; 80 | segmentation->SetMasterRepresentationName(vtkSegmentationConverter::GetClosedSurfaceRepresentationName()); 81 | segmentation->AddSegment(sphereSegment1); 82 | segmentation->AddSegment(sphereSegment2); 83 | segmentation->AddSegment(sphereSegment3); 84 | SetReferenceGeometry(segmentation); 85 | 86 | int numClosedSurfaceLayers = segmentation->GetNumberOfLayers(vtkSegmentationConverter::GetClosedSurfaceRepresentationName()); 87 | if (numClosedSurfaceLayers != 3) 88 | { 89 | std::cerr << __LINE__ << ": Invalid number of closed surface layers " << numClosedSurfaceLayers 90 | << " should be 3" << std::endl; 91 | return false; 92 | } 93 | 94 | segmentation->CreateRepresentation(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName()); 95 | 96 | int numBinaryLabelmapLayers = segmentation->GetNumberOfLayers(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName()); 97 | if (numBinaryLabelmapLayers != 2) 98 | { 99 | std::cerr << __LINE__ << ": Invalid number of binary labelmap layers " << numBinaryLabelmapLayers 100 | << " should be 2" << std::endl; 101 | return false; 102 | } 103 | 104 | std::vector sharedSegmentIDs; 105 | segmentation->GetSegmentIDsSharingBinaryLabelmapRepresentation(segmentation->GetSegmentIdBySegment(sphereSegment1), sharedSegmentIDs, true); 106 | if (sharedSegmentIDs.size() != 2) 107 | { 108 | std::cerr << __LINE__ << ": Invalid number of shared labelmaps for segment " << sphereSegment1->GetName() 109 | << ": " << sharedSegmentIDs.size() << " should be 2" << std::endl; 110 | return false; 111 | } 112 | 113 | segmentation->GetSegmentIDsSharingBinaryLabelmapRepresentation(segmentation->GetSegmentIdBySegment(sphereSegment2), sharedSegmentIDs, true); 114 | if (sharedSegmentIDs.size() != 1) 115 | { 116 | std::cerr << __LINE__ << ": Invalid number of shared labelmaps for segment " << sphereSegment2->GetName() 117 | << ": " << sharedSegmentIDs.size() << " should be 1" << std::endl; 118 | return false; 119 | } 120 | 121 | return true; 122 | } 123 | 124 | bool TestSharedLabelmapCollapse() 125 | { 126 | vtkNew cubeImage1; 127 | int extent1[6] = { 0, 2, 0, 2, 0, 2 }; 128 | int imageCount1 = CreateCubeLabelmap(cubeImage1, extent1); 129 | 130 | vtkNew cubeImage2; 131 | int extent2[6] = { -2, 2, -2, 2, -2, 2 }; 132 | int imageCount2 = CreateCubeLabelmap(cubeImage2, extent2); 133 | 134 | vtkNew cubeImage3; 135 | int extent3[6] = { 0, 2, 0, 2, 0, 2 }; 136 | int imageCount3 = CreateCubeLabelmap(cubeImage3, extent3); 137 | 138 | vtkNew cubeImage4; 139 | int extent4[6] = { 3, 5, 3, 5, 3, 5 }; 140 | int imageCount4 = CreateCubeLabelmap(cubeImage4, extent4); 141 | 142 | vtkNew segment1; 143 | segment1->SetName("cube1"); 144 | segment1->AddRepresentation(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName(), cubeImage1); 145 | 146 | vtkNew segment2; 147 | segment2->SetName("cube2"); 148 | segment2->AddRepresentation(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName(), cubeImage2); 149 | 150 | vtkNew segment3; 151 | segment3->SetName("cube3"); 152 | segment3->AddRepresentation(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName(), cubeImage3); 153 | 154 | vtkNew segment4; 155 | segment4->SetName("cube4"); 156 | segment4->AddRepresentation(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName(), cubeImage4); 157 | 158 | std::vector segments = 159 | { 160 | segment1, 161 | segment2, 162 | segment3, 163 | segment4, 164 | }; 165 | 166 | vtkNew segmentation; 167 | segmentation->SetMasterRepresentationName(vtkSegmentationConverter::GetBinaryLabelmapRepresentationName()); 168 | segmentation->AddSegment(segment1); 169 | segmentation->AddSegment(segment2); 170 | segmentation->AddSegment(segment3); 171 | segmentation->AddSegment(segment4); 172 | 173 | int numberOfLayers = 0; 174 | 175 | numberOfLayers = segmentation->GetNumberOfLayers(); 176 | if (numberOfLayers != 4) 177 | { 178 | std::cerr << "Invalid number of layers " << numberOfLayers << " should be 4" << std::endl; 179 | return false; 180 | } 181 | 182 | ///////////////////////////////////// 183 | segmentation->CollapseBinaryLabelmaps(false); // Merge to multiple layers 184 | 185 | numberOfLayers = segmentation->GetNumberOfLayers(); 186 | if (numberOfLayers != 3) 187 | { 188 | std::cerr << "Safe merge failed: Invalid number of layers " << numberOfLayers << " should be 3" << std::endl; 189 | return false; 190 | } 191 | 192 | vtkNew imageAccumulate; 193 | double frequency = 0.0; 194 | 195 | std::vector expectedResults = 196 | { 197 | imageCount1, 198 | imageCount2, 199 | imageCount3, 200 | imageCount4, 201 | }; 202 | for (int i = 0; i < segments.size(); ++i) 203 | { 204 | vtkSegment* segment = segments[i]; 205 | int expectedFrequency = expectedResults[i]; 206 | 207 | vtkOrientedImageData* segmentLabelmap = vtkOrientedImageData::SafeDownCast(segment->GetRepresentation( 208 | vtkSegmentationConverter::GetBinaryLabelmapRepresentationName())); 209 | double labelValue = segment->GetLabelValue(); 210 | imageAccumulate->SetInputData(segmentLabelmap); 211 | imageAccumulate->Update(); 212 | frequency = imageAccumulate->GetOutput()->GetPointData()->GetScalars()->GetTuple1((vtkIdType)labelValue); 213 | if (frequency != expectedFrequency) 214 | { 215 | std::cerr << "Invalid number of voxels in " << segment->GetName() << " " << frequency << " should be " << expectedFrequency << std::endl; 216 | return false; 217 | } 218 | } 219 | 220 | ///////////////////////////////////// 221 | segmentation->CollapseBinaryLabelmaps(true); // Overwrite merge 222 | 223 | numberOfLayers = segmentation->GetNumberOfLayers(); 224 | if (numberOfLayers != 1) 225 | { 226 | std::cerr << "Overwrite merge failed: Invalid number of layers " << numberOfLayers << " should be 1" << std::endl; 227 | return false; 228 | } 229 | 230 | 231 | expectedResults = 232 | { 233 | 0, 234 | imageCount2 - imageCount3, 235 | imageCount3, 236 | imageCount4, 237 | }; 238 | for (int i = 0; i < segments.size(); ++i) 239 | { 240 | vtkSegment* segment = segments[i]; 241 | int expectedFrequency = expectedResults[i]; 242 | 243 | vtkOrientedImageData* segmentLabelmap = vtkOrientedImageData::SafeDownCast(segment->GetRepresentation( 244 | vtkSegmentationConverter::GetBinaryLabelmapRepresentationName())); 245 | double labelValue = segment->GetLabelValue(); 246 | imageAccumulate->SetInputData(segmentLabelmap); 247 | imageAccumulate->Update(); 248 | frequency = imageAccumulate->GetOutput()->GetPointData()->GetScalars()->GetTuple1((vtkIdType)labelValue); 249 | if (frequency != expectedFrequency) 250 | { 251 | std::cerr << "Invalid number of voxels in " << segment->GetName() << " " << frequency << " should be " << expectedFrequency << std::endl; 252 | return false; 253 | } 254 | } 255 | 256 | ///////////////////////////////////// 257 | segmentation->SeparateSegmentLabelmap(segmentation->GetSegmentIdBySegment(segment1)); 258 | 259 | numberOfLayers = segmentation->GetNumberOfLayers(); 260 | if (numberOfLayers != 2) 261 | { 262 | std::cerr << "Separate segment labelmap failed: Invalid number of layers " << numberOfLayers << " should be 2" << std::endl; 263 | return false; 264 | } 265 | 266 | return true; 267 | } 268 | 269 | //---------------------------------------------------------------------------- 270 | bool TestSharedLabelmapCasting() 271 | { 272 | vtkNew labelmap; 273 | labelmap->SetDimensions(1, 1, 1); 274 | labelmap->AllocateScalars(VTK_UNSIGNED_CHAR, 1); 275 | 276 | vtkNew segment; 277 | segment->SetName("Segment"); 278 | segment->AddRepresentation(vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName(), labelmap); 279 | 280 | vtkNew segmentation; 281 | segmentation->AddSegment(segment); 282 | 283 | if (labelmap->GetScalarType() != VTK_UNSIGNED_CHAR) 284 | { 285 | std::cerr << "Invalid scalar type " << labelmap->GetScalarType() << " should be " << VTK_UNSIGNED_CHAR << std::endl; 286 | return false; 287 | } 288 | 289 | for (int i = 0; i < 254; ++i) 290 | { 291 | segmentation->AddEmptySegment(); 292 | } 293 | 294 | if (labelmap->GetScalarType() != VTK_UNSIGNED_CHAR) 295 | { 296 | std::cerr << "Invalid scalar type " << labelmap->GetScalarType() << " should be " << VTK_UNSIGNED_CHAR << std::endl; 297 | return false; 298 | } 299 | 300 | segmentation->AddEmptySegment(); 301 | if (labelmap->GetScalarType() != VTK_UNSIGNED_SHORT) 302 | { 303 | std::cerr << "Invalid scalar type " << labelmap->GetScalarType() << " should be " << VTK_UNSIGNED_SHORT << std::endl; 304 | return false; 305 | } 306 | 307 | vtkOrientedImageDataResample::CastImageForValue(labelmap, VTK_UNSIGNED_SHORT_MAX + 1); 308 | if (labelmap->GetScalarType() != VTK_UNSIGNED_INT) 309 | { 310 | std::cerr << "Invalid scalar type " << labelmap->GetScalarType() << " should be " << VTK_UNSIGNED_INT << std::endl; 311 | return false; 312 | } 313 | 314 | return true; 315 | } 316 | 317 | //---------------------------------------------------------------------------- 318 | int vtkSegmentationTest2(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) 319 | { 320 | // Register converter rules 321 | vtkSegmentationConverterFactory::GetInstance()->RegisterConverterRule( 322 | vtkSmartPointer::New() ); 323 | vtkSegmentationConverterFactory::GetInstance()->RegisterConverterRule( 324 | vtkSmartPointer::New() ); 325 | 326 | if (!TestSharedLabelmapConversion()) 327 | { 328 | return EXIT_FAILURE; 329 | } 330 | 331 | if (!TestSharedLabelmapCollapse()) 332 | { 333 | return EXIT_FAILURE; 334 | } 335 | 336 | if (!TestSharedLabelmapCasting()) 337 | { 338 | return EXIT_FAILURE; 339 | } 340 | 341 | std::cout << "Segmentation test 2 passed." << std::endl; 342 | return EXIT_SUCCESS; 343 | } 344 | 345 | void SetReferenceGeometry(vtkSegmentation* segmentation) 346 | { 347 | //// Create non-default reference geometry for conversion 348 | vtkNew referenceGeometryMatrix; 349 | referenceGeometryMatrix->Identity(); 350 | referenceGeometryMatrix->SetElement(0, 0, 0.1); 351 | referenceGeometryMatrix->SetElement(1, 1, 0.1); 352 | referenceGeometryMatrix->SetElement(2, 2, 0.1); 353 | int referenceGeometryExtent[6] = { -50,50,-50,50,-50,50 }; 354 | std::string referenceGeometryString = vtkSegmentationConverter::SerializeImageGeometry(referenceGeometryMatrix.GetPointer(), referenceGeometryExtent); 355 | segmentation->SetConversionParameter( 356 | vtkSegmentationConverter::GetReferenceImageGeometryParameterName(), referenceGeometryString); 357 | } 358 | 359 | //---------------------------------------------------------------------------- 360 | void CreateSpherePolyData(vtkPolyData* polyData, double center[3], double radius) 361 | { 362 | vtkNew sphere; 363 | sphere->SetCenter(center); 364 | sphere->SetRadius(radius); 365 | sphere->Update(); 366 | polyData->ShallowCopy(sphere->GetOutput()); 367 | } 368 | 369 | //---------------------------------------------------------------------------- 370 | int CreateCubeLabelmap(vtkOrientedImageData* imageData, int extent[6]) 371 | { 372 | // Create new one because by default the direction is identity, origin is zeros and spacing is ones 373 | vtkNew identityImageData; 374 | imageData->SetExtent(extent); 375 | imageData->AllocateScalars(VTK_UNSIGNED_CHAR, 1); 376 | 377 | unsigned char* imagePtr = (unsigned char*)imageData->GetScalarPointer(); 378 | int size = (extent[1] - extent[0] + 1) * (extent[3] - extent[2] + 1) * (extent[5] - extent[4] + 1); 379 | memset(imagePtr, 1, ( size * imageData->GetScalarSize() * imageData->GetNumberOfScalarComponents())); 380 | return size; 381 | } 382 | -------------------------------------------------------------------------------- /src/vtkBinaryLabelmapToClosedSurfaceConversionRule.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkBinaryLabelmapToClosedSurfaceConversionRule_h 21 | #define __vtkBinaryLabelmapToClosedSurfaceConversionRule_h 22 | 23 | // SegmentationCore includes 24 | #include "vtkSegmentationConverterRule.h" 25 | #include "vtkSegmentationConverter.h" 26 | 27 | #include "PolySegConfigure.h" 28 | 29 | // VTK includes 30 | #include 31 | 32 | /// \ingroup SegmentationCore 33 | /// \brief Convert binary labelmap representation (vtkOrientedImageData type) to 34 | /// closed surface representation (vtkPolyData type). The conversion algorithm 35 | /// performs a marching cubes operation on the image data followed by an optional 36 | /// decimation step. 37 | class PolySeg_EXPORT vtkBinaryLabelmapToClosedSurfaceConversionRule 38 | : public vtkSegmentationConverterRule 39 | { 40 | public: 41 | /// Conversion parameter: decimation factor 42 | static const std::string GetDecimationFactorParameterName() { return "Decimation factor"; }; 43 | /// Conversion parameter: smoothing factor 44 | static const std::string GetSmoothingFactorParameterName() { return "Smoothing factor"; }; 45 | /// Conversion parameter: compute surface normals 46 | static const std::string GetComputeSurfaceNormalsParameterName() { return "Compute surface normals"; }; 47 | /// Conversion parameter: joint smoothing 48 | /// If joint smoothing is enabled, surfaces will be created and smoothed as one vtkPolyData. 49 | /// Joint smoothing converts all segments in shared labelmap together, reducing smoothing artifacts. 50 | static const std::string GetJointSmoothingParameterName() { return "Joint smoothing"; }; 51 | 52 | public: 53 | static vtkBinaryLabelmapToClosedSurfaceConversionRule* New(); 54 | vtkTypeMacro(vtkBinaryLabelmapToClosedSurfaceConversionRule, vtkSegmentationConverterRule); 55 | vtkSegmentationConverterRule* CreateRuleInstance() override; 56 | 57 | /// Constructs representation object from representation name for the supported representation classes 58 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 59 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 60 | vtkDataObject* ConstructRepresentationObjectByRepresentation(std::string representationName) override; 61 | 62 | /// Constructs representation object from class name for the supported representation classes 63 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 64 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 65 | vtkDataObject* ConstructRepresentationObjectByClass(std::string className) override; 66 | 67 | /// Perform the actual binary labelmap to closed surface conversion 68 | bool CreateClosedSurface(vtkOrientedImageData* inputImage, vtkPolyData* outputPolydata, std::vector values); 69 | 70 | /// Update the target representation based on the source representation 71 | bool Convert(vtkSegment* segment) override; 72 | 73 | /// Perform postprocesing steps on the output 74 | /// Clears the joint smoothing cache 75 | bool PostConvert(vtkSegmentation* segmentation) override; 76 | 77 | /// Get the cost of the conversion. 78 | unsigned int GetConversionCost(vtkDataObject* sourceRepresentation=nullptr, vtkDataObject* targetRepresentation=nullptr) override; 79 | 80 | /// Human-readable name of the converter rule 81 | const char* GetName() override { return "Binary labelmap to closed surface"; }; 82 | 83 | /// Human-readable name of the source representation 84 | const char* GetSourceRepresentationName() override { return vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName(); }; 85 | 86 | /// Human-readable name of the target representation 87 | const char* GetTargetRepresentationName() override { return vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(); }; 88 | 89 | protected: 90 | /// If input labelmap has non-background border voxels, then those regions remain open in the output closed surface. 91 | /// This function checks whether this is the case. 92 | bool IsLabelmapPaddingNecessary(vtkImageData* binaryLabelMap); 93 | 94 | protected: 95 | vtkBinaryLabelmapToClosedSurfaceConversionRule(); 96 | ~vtkBinaryLabelmapToClosedSurfaceConversionRule() override; 97 | void operator=(const vtkBinaryLabelmapToClosedSurfaceConversionRule&); 98 | 99 | protected: 100 | /// Cache for storing merged closed surfaces that have been joint smoothed 101 | /// The key used is the binary labelmap representation, which maps to the combined vtkPolyData containing surfaces for all segments in the segmentation 102 | std::map > JointSmoothCache; 103 | 104 | }; 105 | 106 | #endif // __vtkBinaryLabelmapToClosedSurfaceConversionRule_h 107 | -------------------------------------------------------------------------------- /src/vtkCalculateOversamplingFactor.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | // .NAME vtkCalculateOversamplingFactor - Calculate oversampling factor based on model properties 21 | // .SECTION Description 22 | 23 | #ifndef __vtkCalculateOversamplingFactor_h 24 | #define __vtkCalculateOversamplingFactor_h 25 | 26 | // VTK includes 27 | #include 28 | #include 29 | #include 30 | 31 | // SegmentationCore includes 32 | #include "vtkOrientedImageData.h" 33 | 34 | #include "PolySegConfigure.h" 35 | 36 | class vtkPiecewiseFunction; 37 | 38 | /// \ingroup SegmentationCore 39 | /// \brief Calculate oversampling factor based on model properties using fuzzy logics 40 | class PolySeg_EXPORT vtkCalculateOversamplingFactor : public vtkObject 41 | { 42 | public: 43 | static vtkCalculateOversamplingFactor *New(); 44 | vtkTypeMacro(vtkCalculateOversamplingFactor, vtkObject); 45 | void PrintSelf(ostream& os, vtkIndent indent) override; 46 | 47 | public: 48 | /// Calculate oversampling factor for the input model and its rasterization reference volume 49 | /// based on model properties using fuzzy logics. 50 | bool CalculateOversamplingFactor(); 51 | 52 | /// Apply oversampling factor on image data geometry. 53 | /// Changes spacing and extent of oversampling factor is not 1 (and between 0.01 - 100.0). 54 | /// Larger value results larger resulting image extent (and finer resolution). 55 | /// Does not allocate memory, just updates geometry. 56 | static void ApplyOversamplingOnImageGeometry(vtkOrientedImageData* imageData, double oversamplingFactor); 57 | 58 | protected: 59 | /// Calculate relative structure size from input model and rasterization reference volume 60 | /// \return Success flag 61 | bool CalculateRelativeStructureSize(); 62 | 63 | /// Calculate complexity measure based on surface poly data in input model 64 | /// \return Success flag 65 | bool CalculateComplexityMeasure(); 66 | 67 | /// Use fuzzy rules to determine oversampling factor based on calculated relative structure size and complexity measure 68 | /// \return Automatically calculated oversampling factor 69 | double DetermineOversamplingFactor(); 70 | 71 | /// Clip a membership function with the clip value 72 | /// This means that the values of the membership function will be maximized at the clip value, 73 | /// while the function remains the same otherwise (0 values, slopes). 74 | /// \param membershipFunction Membership function to clip 75 | /// \param clipValue Clip value 76 | void ClipMembershipFunction(vtkPiecewiseFunction* membershipFunction, double clipValue); 77 | 78 | public: 79 | vtkGetObjectMacro(InputPolyData, vtkPolyData); 80 | vtkSetObjectMacro(InputPolyData, vtkPolyData); 81 | 82 | vtkGetObjectMacro(ReferenceGeometryImageData, vtkOrientedImageData); 83 | vtkSetObjectMacro(ReferenceGeometryImageData, vtkOrientedImageData); 84 | 85 | vtkGetMacro(OutputOversamplingFactor, double); 86 | 87 | vtkGetMacro(OutputRelativeStructureSize, double); 88 | vtkGetMacro(OutputComplexityMeasure, double); 89 | vtkGetMacro(OutputNormalizedShapeIndex, double); 90 | 91 | vtkGetMacro(LogSpeedMeasurements, bool); 92 | vtkSetMacro(LogSpeedMeasurements, bool); 93 | vtkBooleanMacro(LogSpeedMeasurements, bool); 94 | 95 | protected: 96 | vtkGetObjectMacro(MassPropertiesAlgorithm, vtkMassProperties); 97 | vtkSetObjectMacro(MassPropertiesAlgorithm, vtkMassProperties); 98 | 99 | protected: 100 | /// Input poly data to rasterize 101 | vtkPolyData* InputPolyData; 102 | 103 | /// Image containing the rasterization reference geometry 104 | vtkOrientedImageData* ReferenceGeometryImageData; 105 | 106 | /// Calculated oversampling factor for the segmentation node and its reference volume 107 | double OutputOversamplingFactor; 108 | 109 | /// Calculated relative structure size 110 | double OutputRelativeStructureSize; 111 | /// Calculated complexity measure 112 | double OutputComplexityMeasure; 113 | /// Calculated normalized shape index (NSI), for debugging purposes 114 | double OutputNormalizedShapeIndex; 115 | 116 | /// Flag telling whether the speed measurements are logged on standard output 117 | bool LogSpeedMeasurements; 118 | 119 | /// Temporary storage for mass properties algorithm that is used in both sub-calculations 120 | /// \sa CalculateRelativeStructureSize and CalculateComplexityMeasure 121 | vtkMassProperties* MassPropertiesAlgorithm; 122 | 123 | protected: 124 | vtkCalculateOversamplingFactor(); 125 | ~vtkCalculateOversamplingFactor() override; 126 | 127 | private: 128 | vtkCalculateOversamplingFactor(const vtkCalculateOversamplingFactor&) = delete; 129 | void operator=(const vtkCalculateOversamplingFactor&) = delete; 130 | //ETX 131 | }; 132 | 133 | #endif 134 | 135 | -------------------------------------------------------------------------------- /src/vtkClosedSurfaceToBinaryLabelmapConversionRule.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkClosedSurfaceToBinaryLabelmapConversionRule_h 21 | #define __vtkClosedSurfaceToBinaryLabelmapConversionRule_h 22 | 23 | // SegmentationCore includes 24 | #include "vtkSegmentationConverterRule.h" 25 | #include "vtkSegmentationConverter.h" 26 | 27 | #include "PolySegConfigure.h" 28 | 29 | class vtkPolyData; 30 | 31 | /// \ingroup SegmentationCore 32 | /// \brief Convert closed surface representation (vtkPolyData type) to binary 33 | /// labelmap representation (vtkOrientedImageData type). The conversion algorithm 34 | /// is based on image stencil. 35 | class PolySeg_EXPORT vtkClosedSurfaceToBinaryLabelmapConversionRule 36 | : public vtkSegmentationConverterRule 37 | { 38 | public: 39 | /// Conversion parameter: oversampling factor 40 | /// Determines the oversampling of the reference image geometry. If it's a number, then all segments 41 | /// are oversampled with the same value (value of 1 means no oversampling). If it has the value "A", 42 | /// then automatic oversampling is calculated. 43 | static const std::string GetOversamplingFactorParameterName() { return "Oversampling factor"; }; 44 | static const std::string GetCropToReferenceImageGeometryParameterName() { return "Crop to reference image geometry"; }; 45 | /// Determines if the output binary labelmaps should be reduced to as few shared labelmaps as possible after conversion. 46 | /// A value of 1 means that the labelmaps will be collapsed, while a value of 0 means that they will not be collapsed. 47 | static const std::string GetCollapseLabelmapsParameterName() { return "Collapse labelmaps"; }; 48 | 49 | public: 50 | static vtkClosedSurfaceToBinaryLabelmapConversionRule* New(); 51 | vtkTypeMacro(vtkClosedSurfaceToBinaryLabelmapConversionRule, vtkSegmentationConverterRule); 52 | vtkSegmentationConverterRule* CreateRuleInstance() override; 53 | 54 | /// Constructs representation object from representation name for the supported representation classes 55 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 56 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 57 | vtkDataObject* ConstructRepresentationObjectByRepresentation(std::string representationName) override; 58 | 59 | /// Constructs representation object from class name for the supported representation classes 60 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 61 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 62 | vtkDataObject* ConstructRepresentationObjectByClass(std::string className) override; 63 | 64 | /// Update the target representation based on the source representation 65 | bool Convert(vtkSegment* segment) override; 66 | 67 | /// Perform postprocesing steps on the output 68 | /// Collapses the segments to as few labelmaps as is possible 69 | bool PostConvert(vtkSegmentation* segmentation) override; 70 | 71 | /// Get the cost of the conversion. 72 | unsigned int GetConversionCost(vtkDataObject* sourceRepresentation=nullptr, vtkDataObject* targetRepresentation=nullptr) override; 73 | 74 | /// Human-readable name of the converter rule 75 | const char* GetName() override { return "Closed surface to binary labelmap (simple image stencil)"; }; 76 | 77 | /// Human-readable name of the source representation 78 | const char* GetSourceRepresentationName() override { return vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(); }; 79 | 80 | /// Human-readable name of the target representation 81 | const char* GetTargetRepresentationName() override { return vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName(); }; 82 | 83 | vtkSetMacro(UseOutputImageDataGeometry, bool); 84 | 85 | protected: 86 | /// Calculate actual geometry of the output labelmap volume by verifying that the reference image geometry 87 | /// encompasses the input surface model, and extending it to the proper directions if necessary. 88 | /// \param closedSurfacePolyData Input closed surface poly data to convert 89 | /// \param geometryImageData Output dummy image data containing output labelmap geometry 90 | /// \return Success flag indicating sane calculated extents 91 | bool CalculateOutputGeometry(vtkPolyData* closedSurfacePolyData, vtkOrientedImageData* geometryImageData); 92 | 93 | /// Get default image geometry string in case of absence of parameter. 94 | /// The default geometry has identity directions and 1 mm uniform spacing, 95 | /// with origin and extent defined using the argument poly data. 96 | /// \param polyData Poly data defining the origin and extent of the default geometry 97 | /// \return Serialized image geometry for input poly data with identity directions and 1 mm spacing. 98 | std::string GetDefaultImageGeometryStringForPolyData(vtkPolyData* polyData); 99 | 100 | protected: 101 | /// Flag determining whether to use the geometry of the given output oriented image data as is, 102 | /// or use the conversion parameters and the extent of the input surface. False by default, 103 | /// because pre-calculating the geometry of the output image data is not trivial and should be done 104 | /// only when there is a specific reason to do that (such as doing the conversion for sub-volumes and 105 | /// then stitching them back together). 106 | bool UseOutputImageDataGeometry; 107 | 108 | protected: 109 | vtkClosedSurfaceToBinaryLabelmapConversionRule(); 110 | ~vtkClosedSurfaceToBinaryLabelmapConversionRule() override; 111 | void operator=(const vtkClosedSurfaceToBinaryLabelmapConversionRule&); 112 | }; 113 | 114 | #endif // __vtkClosedSurfaceToBinaryLabelmapConversionRule_h 115 | -------------------------------------------------------------------------------- /src/vtkClosedSurfaceToFractionalLabelmapConversionRule.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Kyle Sunderland, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | #include "vtkClosedSurfaceToFractionalLabelmapConversionRule.h" 20 | 21 | // SegmentationCore includes 22 | #include "vtkOrientedImageData.h" 23 | #include "vtkPolyDataToFractionalLabelmapFilter.h" 24 | 25 | // VTK includes 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | // SegmentationCore includes 33 | #include "vtkSegment.h" 34 | 35 | //---------------------------------------------------------------------------- 36 | vtkSegmentationConverterRuleNewMacro(vtkClosedSurfaceToFractionalLabelmapConversionRule); 37 | 38 | //---------------------------------------------------------------------------- 39 | vtkClosedSurfaceToFractionalLabelmapConversionRule::vtkClosedSurfaceToFractionalLabelmapConversionRule() 40 | { 41 | this->NumberOfOffsets = 6; 42 | this->UseOutputImageDataGeometry = true; 43 | } 44 | 45 | //---------------------------------------------------------------------------- 46 | vtkClosedSurfaceToFractionalLabelmapConversionRule::~vtkClosedSurfaceToFractionalLabelmapConversionRule() 47 | = default; 48 | 49 | //---------------------------------------------------------------------------- 50 | unsigned int vtkClosedSurfaceToFractionalLabelmapConversionRule::GetConversionCost( 51 | vtkDataObject* vtkNotUsed(sourceRepresentation)/*=nullptr*/, 52 | vtkDataObject* vtkNotUsed(targetRepresentation)/*=nullptr*/) 53 | { 54 | // Rough input-independent guess (ms) 55 | return 7000; 56 | } 57 | 58 | //---------------------------------------------------------------------------- 59 | vtkDataObject* vtkClosedSurfaceToFractionalLabelmapConversionRule::ConstructRepresentationObjectByRepresentation(std::string representationName) 60 | { 61 | if ( !representationName.compare(this->GetSourceRepresentationName()) ) 62 | { 63 | return (vtkDataObject*)vtkPolyData::New(); 64 | } 65 | else if ( !representationName.compare(this->GetTargetRepresentationName()) ) 66 | { 67 | return (vtkDataObject*)vtkOrientedImageData::New(); 68 | } 69 | else 70 | { 71 | return nullptr; 72 | } 73 | } 74 | 75 | //---------------------------------------------------------------------------- 76 | vtkDataObject* vtkClosedSurfaceToFractionalLabelmapConversionRule::ConstructRepresentationObjectByClass(std::string className) 77 | { 78 | if (!className.compare("vtkPolyData")) 79 | { 80 | return (vtkDataObject*)vtkPolyData::New(); 81 | } 82 | else if (!className.compare("vtkOrientedImageData")) 83 | { 84 | return (vtkDataObject*)vtkOrientedImageData::New(); 85 | } 86 | else 87 | { 88 | return nullptr; 89 | } 90 | } 91 | 92 | //---------------------------------------------------------------------------- 93 | bool vtkClosedSurfaceToFractionalLabelmapConversionRule::Convert(vtkSegment* segment) 94 | { 95 | this->CreateTargetRepresentation(segment); 96 | 97 | vtkDataObject* sourceRepresentation = segment->GetRepresentation(this->GetSourceRepresentationName()); 98 | vtkDataObject* targetRepresentation = segment->GetRepresentation(this->GetTargetRepresentationName()); 99 | 100 | // Check validity of source and target representation objects 101 | vtkPolyData* closedSurfacePolyData = vtkPolyData::SafeDownCast(sourceRepresentation); 102 | if (!closedSurfacePolyData) 103 | { 104 | vtkErrorMacro("Convert: Source representation is not a poly data!"); 105 | return false; 106 | } 107 | vtkOrientedImageData* fractionalLabelMap = vtkOrientedImageData::SafeDownCast(targetRepresentation); 108 | if (!fractionalLabelMap) 109 | { 110 | vtkErrorMacro("Convert: Target representation is not an oriented image data!"); 111 | return false; 112 | } 113 | if (closedSurfacePolyData->GetNumberOfPoints() < 2 || closedSurfacePolyData->GetNumberOfCells() < 2) 114 | { 115 | vtkErrorMacro("Convert: Cannot create binary labelmap from surface with number of points: " << closedSurfacePolyData->GetNumberOfPoints() << " and number of cells: " << closedSurfacePolyData->GetNumberOfCells()); 116 | return false; 117 | } 118 | 119 | // Compute output labelmap geometry based on poly data, an reference image 120 | // geometry, and store the calculated geometry in output labelmap image data 121 | if (!this->CalculateOutputGeometry(closedSurfacePolyData, fractionalLabelMap)) 122 | { 123 | vtkErrorMacro("Convert: Failed to calculate output image geometry!"); 124 | return false; 125 | } 126 | 127 | // Pad the extent of the fractional labelmap 128 | int extent[6] = {0,-1,0,-1,0,-1}; 129 | fractionalLabelMap->GetExtent(extent); 130 | for (int i=0; i<2; ++i) 131 | { 132 | --extent[2*i]; 133 | ++extent[2*i+1]; 134 | } 135 | fractionalLabelMap->SetExtent(extent); 136 | 137 | vtkSmartPointer imageToWorldMatrix = vtkSmartPointer::New(); 138 | fractionalLabelMap->GetImageToWorldMatrix(imageToWorldMatrix); 139 | 140 | // Create a fractional labelmap from the closed surface 141 | vtkSmartPointer polyDataToLabelmapFilter = vtkSmartPointer::New(); 142 | polyDataToLabelmapFilter->SetInputData(closedSurfacePolyData); 143 | polyDataToLabelmapFilter->SetOutputImageToWorldMatrix(imageToWorldMatrix); 144 | polyDataToLabelmapFilter->SetNumberOfOffsets(this->NumberOfOffsets); 145 | polyDataToLabelmapFilter->SetOutputWholeExtent(fractionalLabelMap->GetExtent()); 146 | polyDataToLabelmapFilter->Update(); 147 | fractionalLabelMap->DeepCopy(polyDataToLabelmapFilter->GetOutput()); 148 | 149 | // Specify the scalar range of values in the labelmap 150 | vtkSmartPointer scalarRange = vtkSmartPointer::New(); 151 | scalarRange->SetName(vtkSegmentationConverter::GetScalarRangeFieldName()); 152 | scalarRange->InsertNextValue(FRACTIONAL_MIN); 153 | scalarRange->InsertNextValue(FRACTIONAL_MAX); 154 | fractionalLabelMap->GetFieldData()->AddArray(scalarRange); 155 | 156 | // Specify the surface threshold value for visualization 157 | vtkSmartPointer thresholdValue = vtkSmartPointer::New(); 158 | thresholdValue->SetName(vtkSegmentationConverter::GetThresholdValueFieldName()); 159 | thresholdValue->InsertNextValue((FRACTIONAL_MIN+FRACTIONAL_MAX)/2.0); 160 | fractionalLabelMap->GetFieldData()->AddArray(thresholdValue); 161 | 162 | // Specify the interpolation type for visualization 163 | vtkSmartPointer interpolationType = vtkSmartPointer::New(); 164 | interpolationType->SetName(vtkSegmentationConverter::GetInterpolationTypeFieldName()); 165 | interpolationType->InsertNextValue(VTK_LINEAR_INTERPOLATION); 166 | fractionalLabelMap->GetFieldData()->AddArray(interpolationType); 167 | 168 | return true; 169 | } 170 | -------------------------------------------------------------------------------- /src/vtkClosedSurfaceToFractionalLabelmapConversionRule.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Kyle Sunderland, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkClosedSurfaceToFractionalLabelmapConversionRule_h 21 | #define __vtkClosedSurfaceToFractionalLabelmapConversionRule_h 22 | 23 | // SegmentationCore includes 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | // DicomRtImportExport includes 30 | #include "PolySegConfigure.h" 31 | 32 | // VTK includes 33 | #include 34 | 35 | class vtkPolyData; 36 | 37 | /// \ingroup SegmentationCore 38 | /// \brief Convert closed surface representation (vtkPolyData type) to fractional 39 | /// labelmap representation (vtkOrientedImageData type). The conversion algorithm 40 | /// is based on image stencil. 41 | class PolySeg_EXPORT vtkClosedSurfaceToFractionalLabelmapConversionRule 42 | : public vtkClosedSurfaceToBinaryLabelmapConversionRule 43 | { 44 | 45 | public: 46 | static vtkClosedSurfaceToFractionalLabelmapConversionRule* New(); 47 | vtkTypeMacro(vtkClosedSurfaceToFractionalLabelmapConversionRule, vtkClosedSurfaceToBinaryLabelmapConversionRule); 48 | vtkSegmentationConverterRule* CreateRuleInstance() override; 49 | 50 | /// Constructs representation object from representation name for the supported representation classes 51 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 52 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 53 | vtkDataObject* ConstructRepresentationObjectByRepresentation(std::string representationName) override; 54 | 55 | /// Constructs representation object from class name for the supported representation classes 56 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 57 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 58 | vtkDataObject* ConstructRepresentationObjectByClass(std::string className) override; 59 | 60 | /// Update the target representation based on the source representation 61 | bool Convert(vtkSegment* segment) override; 62 | 63 | /// Overridden to prevent vtkClosedSurfaceToBinaryLabelmapConversionRule::PostConvert 64 | bool PostConvert(vtkSegmentation* vtkNotUsed(segmentation)) override { return true; }; 65 | 66 | /// Get the cost of the conversion. 67 | unsigned int GetConversionCost(vtkDataObject* sourceRepresentation=nullptr, vtkDataObject* targetRepresentation=nullptr) override; 68 | 69 | /// Human-readable name of the converter rule 70 | const char* GetName() override { return "Closed surface to fractional labelmap (simple image stencil)"; }; 71 | 72 | /// Human-readable name of the source representation 73 | const char* GetSourceRepresentationName() override { return vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(); }; 74 | 75 | /// Human-readable name of the target representation 76 | const char* GetTargetRepresentationName() override { return vtkSegmentationConverter::GetSegmentationFractionalLabelmapRepresentationName(); }; 77 | 78 | protected: 79 | // Oversampling factor that will be used to calculate the size of the binary labelmap 80 | int NumberOfOffsets; 81 | 82 | protected: 83 | 84 | vtkClosedSurfaceToFractionalLabelmapConversionRule(); 85 | ~vtkClosedSurfaceToFractionalLabelmapConversionRule() override; 86 | void operator=(const vtkClosedSurfaceToFractionalLabelmapConversionRule&); 87 | }; 88 | 89 | #endif // __vtkClosedSurfaceToFractionalLabelmapConversionRule_h 90 | -------------------------------------------------------------------------------- /src/vtkFractionalLabelmapToClosedSurfaceConversionRule.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Kyle Sunderland, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | // VTK includes 21 | #include // must precede reference to VTK_MAJOR_VERSION 22 | #include 23 | #include 24 | #if VTK_MAJOR_VERSION >= 9 || (VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 2) 25 | #include 26 | #else 27 | #include 28 | #endif 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | // SegmentationCore includes 42 | #include "vtkFractionalLabelmapToClosedSurfaceConversionRule.h" 43 | #include 44 | #include 45 | 46 | //---------------------------------------------------------------------------- 47 | vtkSegmentationConverterRuleNewMacro(vtkFractionalLabelmapToClosedSurfaceConversionRule); 48 | 49 | //---------------------------------------------------------------------------- 50 | vtkFractionalLabelmapToClosedSurfaceConversionRule::vtkFractionalLabelmapToClosedSurfaceConversionRule() 51 | : vtkBinaryLabelmapToClosedSurfaceConversionRule() 52 | { 53 | this->ConversionParameters[this->GetFractionalLabelMapOversamplingFactorParameterName()] = std::make_pair("1", "Determines the oversampling of the reference image geometry. All segments are oversampled with the same value (value of 1 means no oversampling)."); 54 | this->ConversionParameters[this->GetThresholdFractionParameterName()] = std::make_pair("0.5", "Determines the threshold that the closed surface is created at as a fractional value between 0 and 1."); 55 | } 56 | 57 | //---------------------------------------------------------------------------- 58 | vtkFractionalLabelmapToClosedSurfaceConversionRule::~vtkFractionalLabelmapToClosedSurfaceConversionRule() 59 | = default; 60 | 61 | //---------------------------------------------------------------------------- 62 | unsigned int vtkFractionalLabelmapToClosedSurfaceConversionRule::GetConversionCost( 63 | vtkDataObject* vtkNotUsed(sourceRepresentation)/*=nullptr*/, 64 | vtkDataObject* vtkNotUsed(targetRepresentation)/*=nullptr*/) 65 | { 66 | // Rough input-independent guess (ms) 67 | return 600; 68 | } 69 | 70 | //---------------------------------------------------------------------------- 71 | vtkDataObject* vtkFractionalLabelmapToClosedSurfaceConversionRule::ConstructRepresentationObjectByRepresentation(std::string representationName) 72 | { 73 | if ( !representationName.compare(this->GetSourceRepresentationName()) ) 74 | { 75 | return (vtkDataObject*)vtkOrientedImageData::New(); 76 | } 77 | else if ( !representationName.compare(this->GetTargetRepresentationName()) ) 78 | { 79 | return (vtkDataObject*)vtkPolyData::New(); 80 | } 81 | else 82 | { 83 | return nullptr; 84 | } 85 | } 86 | 87 | //---------------------------------------------------------------------------- 88 | vtkDataObject* vtkFractionalLabelmapToClosedSurfaceConversionRule::ConstructRepresentationObjectByClass(std::string className) 89 | { 90 | if (!className.compare("vtkOrientedImageData")) 91 | { 92 | return (vtkDataObject*)vtkOrientedImageData::New(); 93 | } 94 | else if (!className.compare("vtkPolyData")) 95 | { 96 | return (vtkDataObject*)vtkPolyData::New(); 97 | } 98 | else 99 | { 100 | return nullptr; 101 | } 102 | } 103 | 104 | //---------------------------------------------------------------------------- 105 | bool vtkFractionalLabelmapToClosedSurfaceConversionRule::Convert(vtkSegment* segment) 106 | { 107 | this->CreateTargetRepresentation(segment); 108 | 109 | vtkDataObject* sourceRepresentation = segment->GetRepresentation(this->GetSourceRepresentationName()); 110 | vtkDataObject* targetRepresentation = segment->GetRepresentation(this->GetTargetRepresentationName()); 111 | 112 | // Check validity of source and target representation objects 113 | vtkOrientedImageData* fractionalLabelMap = vtkOrientedImageData::SafeDownCast(sourceRepresentation); 114 | if (!fractionalLabelMap) 115 | { 116 | vtkErrorMacro("Convert: Source representation is not an oriented image data!"); 117 | return false; 118 | } 119 | vtkPolyData* closedSurfacePolyData = vtkPolyData::SafeDownCast(targetRepresentation); 120 | if (!closedSurfacePolyData) 121 | { 122 | vtkErrorMacro("Convert: Target representation is not a poly data!"); 123 | return false; 124 | } 125 | 126 | // Get the range of the scalars in the image data from the ScalarRange field if it exists 127 | // Default to the scalar range of 0.0 to 1.0 otherwise 128 | double minimumValue = 0.0; 129 | double maximumValue = 1.0; 130 | fractionalLabelMap->GetFieldData(); 131 | vtkDoubleArray* scalarRange = vtkDoubleArray::SafeDownCast( 132 | fractionalLabelMap->GetFieldData()->GetAbstractArray( vtkSegmentationConverter::GetScalarRangeFieldName() ) 133 | ); 134 | if (scalarRange && scalarRange->GetNumberOfValues() == 2) 135 | { 136 | minimumValue = scalarRange->GetValue(0); 137 | maximumValue = scalarRange->GetValue(1); 138 | } 139 | 140 | // Pad labelmap if it has non-background border voxels 141 | bool paddingNecessary = this->IsLabelmapPaddingNecessary(fractionalLabelMap); 142 | if (paddingNecessary) 143 | { 144 | vtkOrientedImageData* paddedLabelmap = vtkOrientedImageData::New(); 145 | paddedLabelmap->DeepCopy(fractionalLabelMap); 146 | this->PadLabelmap(paddedLabelmap, minimumValue); 147 | fractionalLabelMap = paddedLabelmap; 148 | } 149 | 150 | // Get conversion parameters 151 | double decimationFactor = vtkVariant(this->ConversionParameters[this->GetDecimationFactorParameterName()].first).ToDouble(); 152 | double smoothingFactor = vtkVariant(this->ConversionParameters[this->GetSmoothingFactorParameterName()].first).ToDouble(); 153 | int computeSurfaceNormals = vtkVariant(this->ConversionParameters[GetComputeSurfaceNormalsParameterName()].first).ToInt(); 154 | double fractionalOversamplingFactor = vtkVariant(this->ConversionParameters[this->GetFractionalLabelMapOversamplingFactorParameterName()].first).ToDouble(); 155 | double fractionalThreshold = vtkVariant(this->ConversionParameters[this->GetThresholdFractionParameterName()].first).ToDouble(); 156 | 157 | if (fractionalThreshold < 0 || fractionalThreshold > 1) 158 | { 159 | vtkErrorMacro("Convert: Fractional threshold must be between 0.0 and 1.0!"); 160 | return false; 161 | } 162 | 163 | // Save geometry of oriented image data before conversion so that it can be applied on the poly data afterwards 164 | vtkSmartPointer labelmapImageToWorldMatrix = vtkSmartPointer::New(); 165 | fractionalLabelMap->GetImageToWorldMatrix(labelmapImageToWorldMatrix); 166 | 167 | // Clone labelmap and set identity geometry so that the whole transform can be done in IJK space and then 168 | // the whole transform can be applied on the poly data to transform it to the world coordinate system 169 | vtkSmartPointer fractionalLabelmapWithIdentityGeometry = vtkSmartPointer::New(); 170 | fractionalLabelmapWithIdentityGeometry->ShallowCopy(fractionalLabelMap); 171 | vtkSmartPointer identityMatrix = vtkSmartPointer::New(); 172 | identityMatrix->Identity(); 173 | fractionalLabelmapWithIdentityGeometry->SetGeometryFromImageToWorldMatrix(identityMatrix); 174 | 175 | // Resize the image with interpolation, this helps the conversion for structures with small labelmaps 176 | vtkSmartPointer imageResize = vtkSmartPointer::New(); 177 | imageResize->SetInputData(fractionalLabelmapWithIdentityGeometry); 178 | imageResize->BorderOn(); 179 | imageResize->SetResizeMethodToMagnificationFactors(); 180 | imageResize->SetMagnificationFactors(fractionalOversamplingFactor, fractionalOversamplingFactor, fractionalOversamplingFactor); 181 | imageResize->InterpolateOn(); 182 | 183 | // Run marching cubes 184 | #if VTK_MAJOR_VERSION >= 9 || (VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 2) 185 | vtkSmartPointer marchingCubes = vtkSmartPointer::New(); 186 | #else 187 | vtkSmartPointer marchingCubes = vtkSmartPointer::New(); 188 | #endif 189 | marchingCubes->SetInputConnection(imageResize->GetOutputPort()); 190 | marchingCubes->SetNumberOfContours(1); 191 | marchingCubes->SetValue(0, (fractionalThreshold * (maximumValue - minimumValue)) + minimumValue); 192 | marchingCubes->ComputeScalarsOff(); 193 | marchingCubes->ComputeGradientsOff(); 194 | marchingCubes->ComputeNormalsOff(); 195 | try 196 | { 197 | marchingCubes->Update(); 198 | } 199 | catch(...) 200 | { 201 | vtkErrorMacro("Convert: Error while running marching cubes!"); 202 | return false; 203 | } 204 | 205 | vtkSmartPointer convertedSegment = vtkSmartPointer::New(); 206 | 207 | // Run marching cubes 208 | vtkSmartPointer processingResult = marchingCubes->GetOutput(); 209 | if (processingResult->GetNumberOfPolys() == 0) 210 | { 211 | vtkDebugMacro("Convert: No polygons can be created, probably all voxels are empty"); 212 | convertedSegment = nullptr; 213 | closedSurfacePolyData->Reset(); 214 | } 215 | 216 | if (!convertedSegment) 217 | { 218 | return true; 219 | } 220 | 221 | // Decimate 222 | if (decimationFactor > 0.0) 223 | { 224 | vtkSmartPointer decimator = vtkSmartPointer::New(); 225 | decimator->SetInputData(processingResult); 226 | decimator->SetFeatureAngle(60); 227 | decimator->SplittingOff(); 228 | decimator->PreserveTopologyOn(); 229 | decimator->SetMaximumError(1); 230 | decimator->SetTargetReduction(decimationFactor); 231 | decimator->Update(); 232 | processingResult = decimator->GetOutput(); 233 | } 234 | 235 | if (smoothingFactor > 0) 236 | { 237 | vtkSmartPointer smoother = vtkSmartPointer::New(); 238 | smoother->SetInputData(processingResult); 239 | smoother->SetNumberOfIterations(20); // based on VTK documentation ("Ten or twenty iterations is all the is usually necessary") 240 | // This formula maps: 241 | // 0.0 -> 1.0 (almost no smoothing) 242 | // 0.25 -> 0.1 (average smoothing) 243 | // 0.5 -> 0.01 (more smoothing) 244 | // 1.0 -> 0.001 (very strong smoothing) 245 | double passBand = pow(10.0, -4.0 * smoothingFactor); 246 | smoother->SetPassBand(passBand); 247 | smoother->BoundarySmoothingOff(); 248 | smoother->FeatureEdgeSmoothingOff(); 249 | smoother->NonManifoldSmoothingOn(); 250 | smoother->NormalizeCoordinatesOn(); 251 | smoother->Update(); 252 | processingResult = smoother->GetOutput(); 253 | } 254 | 255 | // Transform the result surface from labelmap IJK to world coordinate system 256 | vtkSmartPointer labelmapGeometryTransform = vtkSmartPointer::New(); 257 | labelmapGeometryTransform->SetMatrix(labelmapImageToWorldMatrix); 258 | 259 | vtkSmartPointer transformPolyDataFilter = vtkSmartPointer::New(); 260 | transformPolyDataFilter->SetInputData(processingResult); 261 | transformPolyDataFilter->SetTransform(labelmapGeometryTransform); 262 | transformPolyDataFilter->Update(); 263 | 264 | if (computeSurfaceNormals > 0) 265 | { 266 | vtkSmartPointer polyDataNormals = vtkSmartPointer::New(); 267 | polyDataNormals->SetInputConnection(transformPolyDataFilter->GetOutputPort()); 268 | polyDataNormals->ConsistencyOn(); // discrete marching cubes may generate inconsistent surface 269 | // We almost always perform smoothing, so splitting would not be able to preserve any sharp features 270 | // (and sharp edges would look like artifacts in the smooth surface). 271 | polyDataNormals->SplittingOff(); 272 | polyDataNormals->Update(); 273 | convertedSegment->ShallowCopy(polyDataNormals->GetOutput()); 274 | } 275 | else 276 | { 277 | transformPolyDataFilter->Update(); 278 | convertedSegment->ShallowCopy(transformPolyDataFilter->GetOutput()); 279 | } 280 | 281 | // Set output 282 | closedSurfacePolyData->ShallowCopy(convertedSegment); 283 | 284 | // Delete temporary padded labelmap if it was created 285 | if (paddingNecessary) 286 | { 287 | fractionalLabelMap->Delete(); 288 | } 289 | 290 | return true; 291 | } 292 | 293 | //---------------------------------------------------------------------------- 294 | void vtkFractionalLabelmapToClosedSurfaceConversionRule::PadLabelmap(vtkOrientedImageData* fractionalLabelMap, double paddingConstant) 295 | { 296 | vtkSmartPointer padder = vtkSmartPointer::New(); 297 | padder->SetInputData(fractionalLabelMap); 298 | padder->SetConstant(paddingConstant); 299 | int extent[6] = {0,-1,0,-1,0,-1}; 300 | fractionalLabelMap->GetExtent(extent); 301 | // Set the output extent to the new size 302 | padder->SetOutputWholeExtent(extent[0]-1, extent[1]+1, extent[2]-1, extent[3]+1, extent[4]-1, extent[5]+1); 303 | padder->Update(); 304 | fractionalLabelMap->vtkImageData::DeepCopy(padder->GetOutput()); 305 | } 306 | -------------------------------------------------------------------------------- /src/vtkFractionalLabelmapToClosedSurfaceConversionRule.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Kyle Sunderland, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkFractionalLabelmapToClosedSurfaceConversionRule_h 21 | #define __vtkFractionalLabelmapToClosedSurfaceConversionRule_h 22 | 23 | // SegmentationCore includes 24 | #include "vtkBinaryLabelmapToClosedSurfaceConversionRule.h" 25 | #include "vtkSegmentationConverter.h" 26 | #include "PolySegConfigure.h" 27 | 28 | /// \ingroup SegmentationCore 29 | /// \brief Convert Fractional labelmap representation (vtkOrientedImageData type) to 30 | /// closed surface representation (vtkPolyData type). The conversion algorithm 31 | /// performs a marching cubes operation on the image data followed by an optional 32 | /// decimation step. 33 | class PolySeg_EXPORT vtkFractionalLabelmapToClosedSurfaceConversionRule 34 | : public vtkBinaryLabelmapToClosedSurfaceConversionRule 35 | { 36 | public: 37 | /// Conversion parameter: magnification factor 38 | static const std::string GetFractionalLabelMapOversamplingFactorParameterName() { return "Fractional labelmap oversampling factor"; }; 39 | static const std::string GetThresholdFractionParameterName() { return "Threshold fraction"; }; 40 | 41 | public: 42 | static vtkFractionalLabelmapToClosedSurfaceConversionRule* New(); 43 | vtkTypeMacro(vtkFractionalLabelmapToClosedSurfaceConversionRule, vtkBinaryLabelmapToClosedSurfaceConversionRule); 44 | vtkSegmentationConverterRule* CreateRuleInstance() override; 45 | 46 | /// Constructs representation object from representation name for the supported representation classes 47 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 48 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 49 | vtkDataObject* ConstructRepresentationObjectByRepresentation(std::string representationName) override; 50 | 51 | /// Constructs representation object from class name for the supported representation classes 52 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 53 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 54 | vtkDataObject* ConstructRepresentationObjectByClass(std::string className) override; 55 | 56 | /// Update the target representation based on the source representation 57 | bool Convert(vtkSegment* segment) override; 58 | 59 | /// Get the cost of the conversion. 60 | unsigned int GetConversionCost(vtkDataObject* sourceRepresentation=nullptr, vtkDataObject* targetRepresentation=nullptr) override; 61 | 62 | /// Human-readable name of the converter rule 63 | const char* GetName() override { return "Fractional labelmap to closed surface"; }; 64 | 65 | /// Human-readable name of the source representation 66 | const char* GetSourceRepresentationName() override { return vtkSegmentationConverter::GetSegmentationFractionalLabelmapRepresentationName(); }; 67 | 68 | /// Human-readable name of the target representation 69 | const char* GetTargetRepresentationName() override { return vtkSegmentationConverter::GetSegmentationClosedSurfaceRepresentationName(); }; 70 | 71 | protected: 72 | 73 | /// This function adds a border around the image that contains the paddingConstant value 74 | /// \param FractionalLabelMap The image that is being padded 75 | /// \param paddingConstant The value that is used to fill the new voxels 76 | void PadLabelmap(vtkOrientedImageData* fractionalLabelMap, double paddingConstant); 77 | 78 | protected: 79 | vtkFractionalLabelmapToClosedSurfaceConversionRule(); 80 | ~vtkFractionalLabelmapToClosedSurfaceConversionRule() override; 81 | void operator=(const vtkFractionalLabelmapToClosedSurfaceConversionRule&); 82 | }; 83 | 84 | #endif // __vtkFractionalLabelmapToClosedSurfaceConversionRule_h 85 | -------------------------------------------------------------------------------- /src/vtkOrientedImageData.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #include "vtkOrientedImageData.h" 21 | 22 | // VTK includes 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | // STD includes 31 | #include 32 | 33 | vtkStandardNewMacro(vtkOrientedImageData); 34 | 35 | //---------------------------------------------------------------------------- 36 | vtkOrientedImageData::vtkOrientedImageData() 37 | { 38 | int i=0,j=0; 39 | for(i=0; i<3; i++) 40 | { 41 | for(j=0; j<3; j++) 42 | { 43 | this->Directions[i][j] = (i == j) ? 1.0 : 0.0; 44 | } 45 | } 46 | } 47 | 48 | //---------------------------------------------------------------------------- 49 | vtkOrientedImageData::~vtkOrientedImageData() 50 | = default; 51 | 52 | //---------------------------------------------------------------------------- 53 | void vtkOrientedImageData::PrintSelf(ostream& os, vtkIndent indent) 54 | { 55 | this->Superclass::PrintSelf(os,indent); 56 | 57 | os << "Directions:\n"; 58 | 59 | int i=0,j=0; 60 | for(i=0; i<3; i++) 61 | { 62 | for(j=0; j<3; j++) 63 | { 64 | os << indent << " " << this->Directions[i][j]; 65 | } 66 | os << indent << "\n"; 67 | } 68 | os << "\n"; 69 | } 70 | 71 | //---------------------------------------------------------------------------- 72 | void vtkOrientedImageData::ShallowCopy(vtkDataObject *dataObject) 73 | { 74 | // Copy orientation information 75 | this->CopyDirections(dataObject); 76 | 77 | // Do superclass (image, origin, spacing) 78 | this->vtkImageData::ShallowCopy(dataObject); 79 | } 80 | 81 | //---------------------------------------------------------------------------- 82 | void vtkOrientedImageData::DeepCopy(vtkDataObject *dataObject) 83 | { 84 | // Copy orientation information 85 | this->CopyDirections(dataObject); 86 | 87 | // Do superclass (image, origin, spacing) 88 | this->vtkImageData::DeepCopy(dataObject); 89 | } 90 | 91 | //---------------------------------------------------------------------------- 92 | void vtkOrientedImageData::CopyDirections(vtkDataObject *dataObject) 93 | { 94 | vtkOrientedImageData *orientedImageData = vtkOrientedImageData::SafeDownCast(dataObject); 95 | if (orientedImageData != nullptr) 96 | { 97 | double dirs[3][3] = {{0.0, 0.0, 0.0}, 98 | {0.0, 0.0, 0.0}, 99 | {0.0, 0.0, 0.0}}; 100 | orientedImageData->GetDirections(dirs); 101 | this->SetDirections(dirs); 102 | } 103 | } 104 | 105 | //---------------------------------------------------------------------------- 106 | void vtkOrientedImageData::SetDirections(double dirs[3][3]) 107 | { 108 | bool isModified = false; 109 | for (int i=0; i<3; i++) 110 | { 111 | for (int j=0; j<3; j++) 112 | { 113 | if (!vtkMathUtilities::FuzzyCompare(this->Directions[i][j], dirs[i][j])) 114 | { 115 | this->Directions[i][j] = dirs[i][j]; 116 | isModified = true; 117 | } 118 | } 119 | } 120 | if (isModified) 121 | { 122 | this->Modified(); 123 | } 124 | } 125 | 126 | //---------------------------------------------------------------------------- 127 | void vtkOrientedImageData::SetDirections(double ir, double jr, double kr, 128 | double ia, double ja, double ka, 129 | double is, double js, double ks) 130 | { 131 | double dirs[3][3] = {{ir, jr, kr}, 132 | {ia, ja, ka}, 133 | {is, js, ks}}; 134 | this->SetDirections(dirs); 135 | } 136 | 137 | //---------------------------------------------------------------------------- 138 | void vtkOrientedImageData::GetDirections(double dirs[3][3]) 139 | { 140 | for (int i=0; i<3; i++) 141 | { 142 | for (int j=0; j<3; j++) 143 | { 144 | dirs[i][j] = this->Directions[i][j]; 145 | } 146 | } 147 | } 148 | 149 | //---------------------------------------------------------------------------- 150 | double vtkOrientedImageData::GetMinSpacing() 151 | { 152 | if (this->GetSpacing() == nullptr) 153 | { 154 | return 0; 155 | } 156 | double minSpace = this->GetSpacing()[0]; 157 | for(int i = 1; i < 3; ++i) 158 | { 159 | minSpace = std::min(this->GetSpacing()[i], minSpace); 160 | } 161 | return minSpace; 162 | } 163 | 164 | //---------------------------------------------------------------------------- 165 | double vtkOrientedImageData::GetMaxSpacing() 166 | { 167 | if (this->GetSpacing() == nullptr) 168 | { 169 | return 0; 170 | } 171 | double maxSpace = this->GetSpacing()[0]; 172 | for(int i = 1; i < 3; ++i) 173 | { 174 | maxSpace = std::max(this->GetSpacing()[i], maxSpace); 175 | } 176 | return maxSpace; 177 | } 178 | 179 | //---------------------------------------------------------------------------- 180 | void vtkOrientedImageData::SetImageToWorldMatrix(vtkMatrix4x4* argMat) 181 | { 182 | if (argMat == nullptr) 183 | { 184 | return; 185 | } 186 | vtkNew mat; 187 | mat->DeepCopy(argMat); 188 | bool isModified = false; 189 | 190 | // normalize direction vectors 191 | int col=0; 192 | for (col=0; col<3; col++) 193 | { 194 | double len=0.0; 195 | int row=0; 196 | for (row=0; row<3; row++) 197 | { 198 | len += mat->GetElement(row, col) * mat->GetElement(row, col); 199 | } 200 | len = sqrt(len); 201 | 202 | // Set spacing 203 | if (!vtkMathUtilities::FuzzyCompare(this->Spacing[col], len)) 204 | { 205 | this->Spacing[col] = len; 206 | isModified = true; 207 | } 208 | 209 | for (row=0; row<3; row++) 210 | { 211 | mat->SetElement(row, col, mat->GetElement(row, col)/len); 212 | } 213 | } 214 | 215 | for (int row=0; row<3; row++) 216 | { 217 | for (int col=0; col<3; col++) 218 | { 219 | if (!vtkMathUtilities::FuzzyCompare(this->Directions[row][col], mat->GetElement(row, col))) 220 | { 221 | this->Directions[row][col] = mat->GetElement(row, col); 222 | isModified = true; 223 | } 224 | } 225 | 226 | // Set origin 227 | if (!vtkMathUtilities::FuzzyCompare(this->Origin[row], mat->GetElement(row, 3))) 228 | { 229 | this->Origin[row] = mat->GetElement(row, 3); 230 | isModified = true; 231 | } 232 | } 233 | 234 | // Only one Modified event 235 | if (isModified) 236 | { 237 | this->Modified(); 238 | } 239 | } 240 | 241 | //---------------------------------------------------------------------------- 242 | void vtkOrientedImageData::SetGeometryFromImageToWorldMatrix(vtkMatrix4x4* argMat) 243 | { 244 | this->SetImageToWorldMatrix(argMat); 245 | } 246 | 247 | //---------------------------------------------------------------------------- 248 | void vtkOrientedImageData::GetImageToWorldMatrix(vtkMatrix4x4* mat) 249 | { 250 | if (mat == nullptr) 251 | { 252 | return; 253 | } 254 | 255 | // this is the full matrix including the spacing and origin 256 | mat->Identity(); 257 | int row=0,col=0; 258 | for (row=0; row<3; row++) 259 | { 260 | for (col=0; col<3; col++) 261 | { 262 | mat->SetElement(row, col, this->Spacing[col] * Directions[row][col]); 263 | } 264 | mat->SetElement(row, 3, this->Origin[row]); 265 | } 266 | } 267 | 268 | //---------------------------------------------------------------------------- 269 | void vtkOrientedImageData::GetWorldToImageMatrix(vtkMatrix4x4* mat) 270 | { 271 | this->GetImageToWorldMatrix(mat); 272 | mat->Invert(); 273 | } 274 | 275 | //---------------------------------------------------------------------------- 276 | void vtkOrientedImageData::SetDirectionMatrix(vtkMatrix4x4* ijkToRASDirectionMatrix) 277 | { 278 | double dirs[3][3] = {{0.0, 0.0, 0.0}, 279 | {0.0, 0.0, 0.0}, 280 | {0.0, 0.0, 0.0}}; 281 | for (int i=0; i<3; i++) 282 | { 283 | for (int j=0; j<3; j++) 284 | { 285 | dirs[i][j] = ijkToRASDirectionMatrix->Element[i][j]; 286 | } 287 | } 288 | this->SetDirections(dirs); 289 | } 290 | 291 | //---------------------------------------------------------------------------- 292 | void vtkOrientedImageData::GetDirectionMatrix(vtkMatrix4x4* ijkToRASDirectionMatrix) 293 | { 294 | double dirs[3][3] = {{0.0, 0.0, 0.0}, 295 | {0.0, 0.0, 0.0}, 296 | {0.0, 0.0, 0.0}}; 297 | this->GetDirections(dirs); 298 | for (int i=0; i<3; i++) 299 | { 300 | for (int j=0; j<3; j++) 301 | { 302 | ijkToRASDirectionMatrix->Element[i][j] = dirs[i][j]; 303 | } 304 | } 305 | } 306 | 307 | //--------------------------------------------------------------------------- 308 | // (Xmin, Xmax, Ymin, Ymax, Zmin, Zmax) 309 | //--------------------------------------------------------------------------- 310 | void vtkOrientedImageData::ComputeBounds() 311 | { 312 | if ( this->GetMTime() <= this->ComputeTime ) 313 | { 314 | return; 315 | } 316 | 317 | // Sanity check 318 | const int* extent = this->Extent; 319 | if ( extent[0] > extent[1] || 320 | extent[2] > extent[3] || 321 | extent[4] > extent[5] ) 322 | { 323 | // Image is empty, indicated by uninitialized bounds 324 | vtkMath::UninitializeBounds(this->Bounds); 325 | return; 326 | } 327 | 328 | // Compute oriented image corners 329 | vtkNew geometryMatrix; 330 | this->GetImageToWorldMatrix(geometryMatrix.GetPointer()); 331 | 332 | vtkBoundingBox boundingBox; 333 | for (int xSide=0; xSide<2; ++xSide) 334 | { 335 | for (int ySide=0; ySide<2; ++ySide) 336 | { 337 | for (int zSide=0; zSide<2; ++zSide) 338 | { 339 | // Get corner point. Loop variables are either 0 or 1, so coordinate is 340 | // either low or high extent bound along that axis 341 | double cornerPointIJK[4] = { 342 | static_cast(extent[xSide]), 343 | static_cast(extent[2+ySide]), 344 | static_cast(extent[4+zSide]), 345 | 1.0 }; 346 | 347 | // Use voxel corner as boundary, not voxel center: 348 | cornerPointIJK[0] += (xSide == 0 ? -0.5 : 0.5); 349 | cornerPointIJK[1] += (ySide == 0 ? -0.5 : 0.5); 350 | cornerPointIJK[2] += (zSide == 0 ? -0.5 : 0.5); 351 | 352 | // Transform IJK coordinate to get the world coordinate 353 | double cornerPointWorld[4] = {0.0,0.0,0.0,0.0}; 354 | geometryMatrix->MultiplyPoint(cornerPointIJK, cornerPointWorld); 355 | 356 | // Determine bounds based on current corner point 357 | boundingBox.AddPoint(cornerPointWorld); 358 | } 359 | } 360 | } 361 | 362 | boundingBox.GetBounds(this->Bounds); 363 | this->ComputeTime.Modified(); 364 | } 365 | 366 | //--------------------------------------------------------------------------- 367 | bool vtkOrientedImageData::IsEmpty() 368 | { 369 | // Empty if extent is uninitialized or otherwise invalid 370 | if (this->Extent[0] > this->Extent[1] || this->Extent[2] > this->Extent[3] || this->Extent[4] > this->Extent[5]) 371 | { 372 | // empty 373 | return true; 374 | } 375 | return false; 376 | } 377 | -------------------------------------------------------------------------------- /src/vtkOrientedImageData.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkOrientedImageData_h 21 | #define __vtkOrientedImageData_h 22 | 23 | // Segmentation includes 24 | #include "PolySegConfigure.h" 25 | 26 | #include "vtkImageData.h" 27 | 28 | class vtkMatrix4x4; 29 | 30 | /// \ingroup SegmentationCore 31 | /// \brief Image data containing orientation information 32 | /// 33 | /// This extends vtkImageData to arbitrary grid orientation. 34 | /// 35 | class PolySeg_EXPORT vtkOrientedImageData : public vtkImageData 36 | { 37 | public: 38 | static vtkOrientedImageData *New(); 39 | vtkTypeMacro(vtkOrientedImageData,vtkImageData); 40 | void PrintSelf(ostream& os, vtkIndent indent) override; 41 | 42 | /// Shallow copy 43 | void ShallowCopy(vtkDataObject *src) override; 44 | /// Deep copy 45 | void DeepCopy(vtkDataObject *src) override; 46 | /// Copy orientation information only 47 | virtual void CopyDirections(vtkDataObject *src); 48 | 49 | public: 50 | /// Set directions only 51 | void SetDirections(double dirs[3][3]); 52 | /// Set directions only 53 | void SetDirections(double ir, double ia, double is, 54 | double jr, double ja, double js, 55 | double kr, double ka, double ks); 56 | 57 | void GetDirections(double dirs[3][3]); 58 | 59 | /// Utility function that returns the min spacing between the 3 orientations 60 | double GetMinSpacing(); 61 | 62 | /// Utility function that returns the max spacing between the 3 orientations 63 | double GetMaxSpacing(); 64 | 65 | /// Get matrix including directions only 66 | void GetDirectionMatrix(vtkMatrix4x4* mat); 67 | /// Set directions by matrix 68 | void SetDirectionMatrix(vtkMatrix4x4* mat); 69 | 70 | /// Get the geometry matrix that includes the spacing and origin information 71 | void GetImageToWorldMatrix(vtkMatrix4x4* mat); 72 | /// Convenience method to set the directions, spacing, and origin from a matrix 73 | void SetImageToWorldMatrix(vtkMatrix4x4* mat); 74 | /// Same as SetImageToWorldMatrix. Kept for backward compatibility. 75 | void SetGeometryFromImageToWorldMatrix(vtkMatrix4x4* mat); 76 | 77 | /// Compute image bounds (xmin,xmax, ymin,ymax, zmin,zmax). 78 | void ComputeBounds() override; 79 | 80 | /// Get the inverse of the geometry matrix 81 | void GetWorldToImageMatrix(vtkMatrix4x4* mat); 82 | 83 | /// Determines whether the image data is empty (if the extent has 0 voxels then it is) 84 | bool IsEmpty(); 85 | 86 | protected: 87 | vtkOrientedImageData(); 88 | ~vtkOrientedImageData() override; 89 | 90 | protected: 91 | /// Direction matrix for the image data 92 | /// These are unit length direction cosines 93 | double Directions[3][3]; 94 | 95 | private: 96 | vtkOrientedImageData(const vtkOrientedImageData&) = delete; 97 | void operator=(const vtkOrientedImageData&) = delete; 98 | }; 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/vtkOrientedImageDataResample.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkOrientedImageDataResample_h 21 | #define __vtkOrientedImageDataResample_h 22 | 23 | // Segmentation includes 24 | #include "PolySegConfigure.h" 25 | 26 | // VTK includes 27 | #include "vtkObject.h" 28 | 29 | // std includes 30 | #include 31 | 32 | class vtkImageData; 33 | class vtkMatrix4x4; 34 | class vtkOrientedImageData; 35 | class vtkTransform; 36 | class vtkAbstractTransform; 37 | 38 | /// \ingroup SegmentationCore 39 | /// \brief Utility functions for resampling oriented image data 40 | class PolySeg_EXPORT vtkOrientedImageDataResample : public vtkObject 41 | { 42 | public: 43 | static vtkOrientedImageDataResample *New(); 44 | vtkTypeMacro(vtkOrientedImageDataResample,vtkObject); 45 | 46 | enum 47 | { 48 | OPERATION_MINIMUM, 49 | OPERATION_MAXIMUM, 50 | OPERATION_MASKING 51 | }; 52 | 53 | /// Resample an oriented image data to match the geometry of a reference geometry matrix. 54 | /// Origin and dimensions are determined from the contents of the input image. 55 | /// \param inputImage Oriented image to resample 56 | /// \param referenceGeometryMatrix Matrix containing the desired geometry 57 | /// \param outputImage Output image 58 | /// \param linearInterpolation True if linear interpolation is requested (fractional labelmap), or false for nearest neighbor (binary labelmap). Default is false. 59 | /// \return Success flag 60 | static bool ResampleOrientedImageToReferenceGeometry(vtkOrientedImageData* inputImage, vtkMatrix4x4* referenceGeometryMatrix, vtkOrientedImageData* outputImage, bool linearInterpolation=false); 61 | 62 | /// Resample an oriented image data to match the geometry of a reference oriented image data 63 | /// \param inputImage Oriented image to resample 64 | /// \param referenceImage Oriented image containing the desired geometry 65 | /// \param outputImage Output image 66 | /// \param linearInterpolation True if linear interpolation is requested (fractional labelmap), or false for nearest neighbor (binary labelmap). Default is false. 67 | /// \param padImage If enabled then it is made sure that the input image's extent fits into the resampled reference image, so if part of the extent is transformed 68 | /// to be outside the reference extent, then it is padded. Disabled by default. 69 | /// \param inputImageTransform If specified then inputImage will be transformed with inputImageTransform before resampled into referenceImage. 70 | /// \return Success flag 71 | static bool ResampleOrientedImageToReferenceOrientedImage(vtkOrientedImageData* inputImage, vtkOrientedImageData* referenceImage, vtkOrientedImageData* outputImage, bool linearInterpolation=false, bool padImage=false, vtkAbstractTransform* inputImageTransform=nullptr, double backgroundValue=0); 72 | 73 | /// Transform an oriented image data using a transform that can be linear or non-linear. 74 | /// Linear: simply multiply the geometry matrix with the applied matrix, extent stays the same 75 | /// Non-linear: calculate new extents and change only the extents when applying deformable transform 76 | /// \param image Oriented image to transform 77 | /// \param transform Input transform 78 | /// \param geometryOnly Only the geometry of the image is changed according to the transform if this flag is turned on. 79 | /// This flag only has an effect if the transform is non-linear, in which case only the extent is changed. Off by default 80 | /// \param alwaysResample If on, then image data will be resampled even if the applied transform is linear 81 | static void TransformOrientedImage(vtkOrientedImageData* image, vtkAbstractTransform* transform, bool geometryOnly=false, bool alwaysResample=false, bool linearInterpolation=false, double backgroundColor[4]=nullptr); 82 | 83 | /// Combines the inputImage and imageToAppend into a new image by max/min operation. The extent will be the union of the two images. 84 | /// Extent can be specified to restrict imageToAppend's extent to a smaller region. 85 | /// inputImage and imageToAppend must have the same geometry, but they may have different extents. 86 | static bool MergeImage(vtkOrientedImageData* inputImage, vtkOrientedImageData* imageToAppend, vtkOrientedImageData* outputImage, int operation, 87 | const int extent[6]=nullptr, double maskThreshold = 0, double fillValue = 1, bool *outputModified=nullptr); 88 | 89 | /// Modifies inputImage in-place by combining with modifierImage using max/min operation. 90 | /// The extent will remain unchanged. 91 | /// Extent can be specified to restrict modifierImage's extent to a smaller region. 92 | /// inputImage and modifierImage must have the same geometry (origin, spacing, directions) and scalar type, but they may have different extents. 93 | static bool ModifyImage(vtkOrientedImageData* inputImage, vtkOrientedImageData* modifierImage, int operation, 94 | const int extent[6] = nullptr, double maskThreshold = 0, double fillValue = 1); 95 | 96 | /// Copy image with clipping to the specified extent 97 | static bool CopyImage(vtkOrientedImageData* imageToCopy, vtkOrientedImageData* outputImage, const int extent[6]=nullptr); 98 | 99 | /// Prints image information. Does not print lots of irrelevant information that default PrintSelf would print. 100 | static void PrintImageInformation(vtkImageData* imageData, ostream& os, vtkIndent indent); 101 | 102 | /// Fills an image with the specified value 103 | /// \param extent The whole extent is filled if extent is not specified 104 | static void FillImage(vtkImageData* image, double fillValue, const int extent[6]=nullptr); 105 | 106 | public: 107 | /// Calculate effective extent of an image: the IJK extent where non-zero voxels are located 108 | static bool CalculateEffectiveExtent(vtkOrientedImageData* image, int effectiveExtent[6], double threshold = 0.0); 109 | 110 | /// Determine if geometries of two oriented image data objects match. 111 | /// Origin, spacing and direction are considered, extent is not. 112 | static bool DoGeometriesMatch(vtkOrientedImageData* image1, vtkOrientedImageData* image2); 113 | 114 | /// Determine if extents of two oriented image data objects match. 115 | static bool DoExtentsMatch(vtkOrientedImageData* image1, vtkOrientedImageData* image2); 116 | 117 | /// Determine if geometries of two oriented image data objects match. 118 | /// Only considers spacing and orientation, origin and extent may be different! 119 | static bool DoGeometriesMatchIgnoreOrigin(vtkOrientedImageData* image1, vtkOrientedImageData* image2); 120 | 121 | /// Transform input extent to determine output extent of an image. Use all bounding box corners, 122 | /// may miss part of the extent in case of non-linear transforms are used. 123 | static void TransformExtent(const int inputExtent[6], vtkAbstractTransform* inputToOutputTransform, int outputExtent[6]); 124 | 125 | /// Transform input bounds to determine output bounds. Use all bounding box corners, 126 | /// may miss part of the extent in case of non-linear transforms are used. 127 | static void TransformBounds(const double inputBounds[6], vtkAbstractTransform* inputToOutputTransform, double outputBounds[6]); 128 | 129 | /// Transform bounds of oriented image data using a linear or non-linear transform 130 | static void TransformOrientedImageDataBounds(vtkOrientedImageData* image, vtkAbstractTransform* transform, double transformedBounds[6]); 131 | 132 | /// Compare the values (with tolerance) between two 4x4 matrices 133 | /// \param lhs Left-hand side matrix to compare 134 | /// \param rhs Right-hand side matrix to compare 135 | static bool IsEqual(vtkMatrix4x4* lhs, vtkMatrix4x4* rhs); 136 | 137 | /// Compare two floating point numbers within tolerance 138 | static bool AreEqualWithTolerance(double a, double b) { return fabs(a - b) < 0.0001; }; 139 | 140 | /// Calculate transform between two oriented image data 141 | static bool GetTransformBetweenOrientedImages(vtkOrientedImageData* image1, vtkOrientedImageData* image2, vtkTransform* image1ToImage2Transform); 142 | 143 | /// Pad an image to entirely contain another image using custom extent to contain 144 | static bool PadImageToContainImage(vtkOrientedImageData* inputImage, vtkOrientedImageData* containedImage, vtkOrientedImageData* outputImage, const int extent[6]); 145 | /// Pad an image to entirely contain another image 146 | static bool PadImageToContainImage(vtkOrientedImageData* inputImage, vtkOrientedImageData* containedImage, vtkOrientedImageData* outputImage); 147 | 148 | /// Determine if a transform is linear and return it if it is. A simple downcast is not enough, as the transform may be 149 | /// a general transform, which can be linear if the concatenation it contains consist of all linear transforms. 150 | /// \param transform Input transform to assess 151 | /// \param linearTransform Output transform in case transform is linear 152 | /// \return True if input is linear, false otherwise. 153 | static bool IsTransformLinear(vtkAbstractTransform* transform, vtkTransform* linearTransform); 154 | 155 | /// Determine if a transform matrix contains shear 156 | static bool DoesTransformMatrixContainShear(vtkMatrix4x4* matrix); 157 | 158 | /// Apply mask image on an input image 159 | /// \param input Input image to apply the mask on 160 | /// \param mask Mask to apply 161 | /// \param notMask If on, the mask is passed through a boolean not before it is used to mask the image. 162 | /// The effect is to pass the input pixels where the mask is zero, and replace the pixels where the 163 | /// mask is non zero 164 | static bool ApplyImageMask(vtkOrientedImageData* input, vtkOrientedImageData* mask, double fillValue, bool notMask = false); 165 | 166 | /// Get the values contained in the labelmap under the mask 167 | /// \param binaryLabelmap Input image to get values from 168 | /// \param mask Mask image to get values under 169 | /// \param extent Can be set to restrict the examined extent to a smaller region. 170 | /// If nullptr, the extent will be the overlapping extent between the label and mask. 171 | /// \param labelValues The values found in the binary labelmap underneath the mask 172 | /// \param maskThreshold Threshold value for the mask. Values above this threshold are considered to be under the mask 173 | static void GetLabelValuesInMask(std::vector& labelValues, vtkOrientedImageData* binaryLabelmap, vtkOrientedImageData* mask, 174 | const int extent[6]=nullptr, int maskThreshold = 0); 175 | 176 | /// Determine if there is a non-zero value in the labelmap under the mask 177 | /// \param binaryLabelmap Input image to get values from 178 | /// \param mask Mask image to get values under 179 | /// \param extent Can be set to restrict the examined extent to a smaller region. 180 | /// If nullptr, the extent will be the overlapping extent between the label and mask. 181 | /// \param maskThreshold Threshold value for the mask. Values above this threshold are considered to be under the mask 182 | static bool IsLabelInMask(vtkOrientedImageData* binaryLabelmap, vtkOrientedImageData* mask, 183 | int extent[6]=nullptr, int maskThreshold=0); 184 | 185 | /// Cast the data type of the image to be able to contain the specified value 186 | /// \param image Image to convert 187 | /// \param value Value that should be representable by the image data type 188 | static void CastImageForValue(vtkOrientedImageData* image, double value); 189 | 190 | protected: 191 | vtkOrientedImageDataResample(); 192 | ~vtkOrientedImageDataResample() override; 193 | 194 | private: 195 | vtkOrientedImageDataResample(const vtkOrientedImageDataResample&) = delete; 196 | void operator=(const vtkOrientedImageDataResample&) = delete; 197 | }; 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /src/vtkPolyDataToFractionalLabelmapFilter.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Kyle Sunderland, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | This file is a modified version of vtkPolyDataToImageStencil.h 19 | 20 | ==============================================================================*/ 21 | 22 | #ifndef vtkPolyDataToFractionalLabelmapFilter_h 23 | #define vtkPolyDataToFractionalLabelmapFilter_h 24 | 25 | 26 | // VTK includes 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | // Segmentations includes 35 | #include 36 | 37 | // std includes 38 | #include 39 | 40 | #include "PolySegConfigure.h" 41 | 42 | // Define the datatype and fractional constants for fractional labelmap conversion based on the value of VTK_FRACTIONAL_DATA_TYPE 43 | #define VTK_FRACTIONAL_DATA_TYPE VTK_CHAR 44 | 45 | #if VTK_FRACTIONAL_DATA_TYPE == VTK_UNSIGNED_CHAR 46 | #define FRACTIONAL_DATA_TYPE VTK_TYPE_NAME_UNSIGNED_CHAR 47 | #define FRACTIONAL_MIN 0 48 | #define FRACTIONAL_MAX 216 49 | #define FRACTIONAL_STEP_SIZE 1 50 | #elif VTK_FRACTIONAL_DATA_TYPE == VTK_CHAR 51 | #define FRACTIONAL_DATA_TYPE VTK_TYPE_NAME_CHAR 52 | #define FRACTIONAL_MIN -108 53 | #define FRACTIONAL_MAX 108 54 | #define FRACTIONAL_STEP_SIZE 1 55 | #elif VTK_FRACTIONAL_DATA_TYPE == VTK_FLOAT 56 | #define FRACTIONAL_DATA_TYPE VTK_TYPE_NAME_FLOAT 57 | #define FRACTIONAL_MIN 0.0 58 | #define FRACTIONAL_MAX 1.0 59 | #define FRACTIONAL_STEP_SIZE (1.0/216.0) 60 | #endif 61 | 62 | class PolySeg_EXPORT vtkPolyDataToFractionalLabelmapFilter : 63 | public vtkPolyDataToImageStencil 64 | { 65 | private: 66 | std::map > LinesCache; 67 | std::map > SliceCache; 68 | std::map PointIdsCache; 69 | std::map NptsCache; 70 | std::map > PointNeighborCountsCache; 71 | 72 | vtkCellLocator* CellLocator; 73 | 74 | vtkOrientedImageData* OutputImageTransformData; 75 | int NumberOfOffsets; 76 | 77 | public: 78 | static vtkPolyDataToFractionalLabelmapFilter* New(); 79 | vtkTypeMacro(vtkPolyDataToFractionalLabelmapFilter, vtkPolyDataToImageStencil); 80 | 81 | virtual vtkOrientedImageData* GetOutput(); 82 | virtual void SetOutput(vtkOrientedImageData* output); 83 | 84 | void SetOutputImageToWorldMatrix(vtkMatrix4x4* imageToWorldMatrix); 85 | void GetOutputImageToWorldMatrix(vtkMatrix4x4* imageToWorldMatrix); 86 | 87 | using Superclass::GetOutputOrigin; 88 | double* GetOutputOrigin() override; 89 | void GetOutputOrigin(double origin[3]) override; 90 | 91 | void SetOutputOrigin(double origin[3]); 92 | void SetOutputOrigin(double x, double y, double z) override; 93 | 94 | using Superclass::GetOutputSpacing; 95 | double* GetOutputSpacing() override; 96 | void GetOutputSpacing(double spacing[3]) override; 97 | 98 | void SetOutputSpacing(double spacing[3]); 99 | void SetOutputSpacing(double x, double y, double z) override; 100 | 101 | 102 | /// This method deletes the currently stored cache variables 103 | void DeleteCache(); 104 | 105 | vtkSetMacro(NumberOfOffsets, int); 106 | vtkGetMacro(NumberOfOffsets, int); 107 | 108 | protected: 109 | vtkPolyDataToFractionalLabelmapFilter(); 110 | ~vtkPolyDataToFractionalLabelmapFilter() override; 111 | 112 | int RequestData(vtkInformation *, vtkInformationVector **, 113 | vtkInformationVector *) override; 114 | vtkOrientedImageData *AllocateOutputData(vtkDataObject *out, int* updateExt); 115 | int FillOutputPortInformation(int, vtkInformation*) override; 116 | 117 | /// Create a binary image stencil for the closed surface within the current extent 118 | /// This method is a modified version of vtkPolyDataToImageStencil::ThreadedExecute 119 | /// \param output Output stencil data 120 | /// \param closedSurface The input surface to be converted 121 | /// \param extent The extent region that is being converted 122 | void FillImageStencilData(vtkImageStencilData *output, vtkPolyData* closedSurface, int extent[6]); 123 | 124 | /// Add the values of the binary labelmap to the fractional labelmap. 125 | /// \param binaryLabelMap Binary labelmap that will be added to the fractional labelmap 126 | /// \param fractionalLabelMap The fractional labelmap that the binary labelmap is added to 127 | void AddBinaryLabelMapToFractionalLabelMap(vtkImageData* binaryLabelMap, vtkImageData* fractionalLabelMap); 128 | 129 | /// Clip the polydata at the specified z coordinate to create a planar contour. 130 | /// This method is a modified version of vtkPolyDataToImageStencil::PolyDataCutter to decrease execution time 131 | /// \param input The closed surface that is being cut 132 | /// \param output Polydata containing the contour lines 133 | /// \param z The z coordinate for the cutting plane 134 | void PolyDataCutter(vtkPolyData *input, vtkPolyData *output, 135 | double z); 136 | 137 | private: 138 | vtkPolyDataToFractionalLabelmapFilter(const vtkPolyDataToFractionalLabelmapFilter&) = delete; 139 | void operator=(const vtkPolyDataToFractionalLabelmapFilter&) = delete; 140 | }; 141 | 142 | #endif 143 | -------------------------------------------------------------------------------- /src/vtkSegment.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | // SegmentationCore includes 21 | #include "vtkSegment.h" 22 | 23 | #include "vtkSegmentationConverterFactory.h" 24 | #include "vtkOrientedImageData.h" 25 | #include "vtkOrientedImageDataResample.h" 26 | 27 | // VTK includes 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | // STD includes 39 | #include 40 | #include 41 | #include 42 | 43 | //---------------------------------------------------------------------------- 44 | const double vtkSegment::SEGMENT_COLOR_INVALID[3] = {0.5, 0.5, 0.5}; 45 | 46 | const char* vtkSegment::GetTerminologyEntryTagName() { return "TerminologyEntry"; }; 47 | 48 | //---------------------------------------------------------------------------- 49 | vtkStandardNewMacro(vtkSegment); 50 | 51 | //---------------------------------------------------------------------------- 52 | vtkSegment::vtkSegment() 53 | { 54 | this->Name = nullptr; 55 | this->Color[0] = SEGMENT_COLOR_INVALID[0]; 56 | this->Color[1] = SEGMENT_COLOR_INVALID[1]; 57 | this->Color[2] = SEGMENT_COLOR_INVALID[2]; 58 | 59 | this->NameAutoGenerated = true; 60 | this->ColorAutoGenerated = true; 61 | 62 | this->LabelValue = 1; 63 | 64 | // Set default terminology Tissue/Tissue from the default Slicer terminology dictionary 65 | this->SetTag( vtkSegment::GetTerminologyEntryTagName(), 66 | "Segmentation category and type - 3D Slicer General Anatomy list~SRT^T-D0050^Tissue~SRT^T-D0050^Tissue~^^~Anatomic codes - DICOM master list~^^~^^"); 67 | } 68 | 69 | //---------------------------------------------------------------------------- 70 | vtkSegment::~vtkSegment() 71 | { 72 | this->RemoveAllRepresentations(); 73 | this->Representations.clear(); 74 | if (this->Name) 75 | { 76 | delete [] this->Name; 77 | this->Name = nullptr; 78 | } 79 | } 80 | 81 | //---------------------------------------------------------------------------- 82 | void vtkSegment::PrintSelf(ostream& os, vtkIndent indent) 83 | { 84 | // vtkObject's PrintSelf prints a long list of registered events, which 85 | // is too long and not useful, therefore we don't call vtkObject::PrintSelf 86 | // but print essential information on the vtkObject base. 87 | os << indent << "Debug: " << (this->Debug ? "On\n" : "Off\n"); 88 | os << indent << "Modified Time: " << this->GetMTime() << "\n"; 89 | 90 | os << indent << "Name: " << (this->Name ? this->Name : "NULL") << "\n"; 91 | os << indent << "Color: (" << this->Color[0] << ", " << this->Color[1] << ", " << this->Color[2] << ")\n"; 92 | 93 | os << indent << "NameAutoGenerated: " << (this->NameAutoGenerated ? "true" : "false") << "\n"; 94 | os << indent << "ColorAutoGenerated: " << (this->ColorAutoGenerated ? "true" : "false") << "\n"; 95 | 96 | RepresentationMap::iterator reprIt; 97 | os << indent << "Representations:\n"; 98 | for (reprIt=this->Representations.begin(); reprIt!=this->Representations.end(); ++reprIt) 99 | { 100 | os << indent.GetNextIndent() << reprIt->first << " "; 101 | vtkDataObject* dataObject = reprIt->second; 102 | if (dataObject) 103 | { 104 | os << dataObject->GetClassName() << "\n"; 105 | vtkImageData* imageData = vtkImageData::SafeDownCast(dataObject); 106 | vtkPolyData* polyData = vtkPolyData::SafeDownCast(dataObject); 107 | if (imageData) 108 | { 109 | vtkOrientedImageDataResample::PrintImageInformation(imageData, os, indent.GetNextIndent()); 110 | } 111 | if (polyData) 112 | { 113 | os << indent.GetNextIndent().GetNextIndent() << "Number of points: " << polyData->GetNumberOfPoints() << "\n"; 114 | os << indent.GetNextIndent().GetNextIndent() << "Number of cells: " << polyData->GetNumberOfCells() << "\n"; 115 | } 116 | } 117 | else 118 | { 119 | os << "(none)\n"; 120 | } 121 | } 122 | 123 | std::map::iterator tagIt; 124 | os << indent << "Tags:\n"; 125 | for (tagIt=this->Tags.begin(); tagIt!=this->Tags.end(); ++tagIt) 126 | { 127 | os << indent.GetNextIndent() << " " << tagIt->first << ": " << tagIt->second << "\n"; 128 | } 129 | } 130 | 131 | //---------------------------------------------------------------------------- 132 | void vtkSegment::ReadXMLAttributes(const char** vtkNotUsed(atts)) 133 | { 134 | // Note: Segment information is read by the storage node 135 | } 136 | 137 | //--------------------------------------------------------------------------- 138 | void vtkSegment::WriteXML(ostream& of, int vtkNotUsed(nIndent)) 139 | { 140 | // Note: Segment info is written by the storage node, this function is not called 141 | 142 | of << "Name=\"" << (this->Name ? this->Name : "NULL") << "\""; 143 | of << "Color:\"(" << this->Color[0] << ", " << this->Color[1] << ", " << this->Color[2] << ")\""; 144 | 145 | of << "NameAutoGenerated=\"" << (this->NameAutoGenerated ? "true" : "false") << "\""; 146 | of << "ColorAutoGenerated=\"" << (this->ColorAutoGenerated ? "true" : "false") << "\""; 147 | 148 | RepresentationMap::iterator reprIt; 149 | of << "Representations=\""; 150 | for (reprIt=this->Representations.begin(); reprIt!=this->Representations.end(); ++reprIt) 151 | { 152 | of << " " << reprIt->first << "\""; 153 | } 154 | 155 | std::map::iterator tagIt; 156 | of << "Tags=\""; 157 | for (tagIt=this->Tags.begin(); tagIt!=this->Tags.end(); ++tagIt) 158 | { 159 | of << tagIt->first << ":" << tagIt->second << "|"; 160 | } 161 | of << "\""; 162 | } 163 | 164 | //---------------------------------------------------------------------------- 165 | void vtkSegment::DeepCopy(vtkSegment* source) 166 | { 167 | if (!source) 168 | { 169 | vtkErrorMacro("vtkSegment::DeepCopy failed: sourceSegment is invalid"); 170 | return; 171 | } 172 | 173 | this->DeepCopyMetadata(source); 174 | 175 | // Deep copy representations 176 | std::set representationNamesToKeep; 177 | RepresentationMap::iterator reprIt; 178 | for (reprIt=source->Representations.begin(); reprIt!=source->Representations.end(); ++reprIt) 179 | { 180 | vtkDataObject* representationCopy = 181 | vtkSegmentationConverterFactory::GetInstance()->ConstructRepresentationObjectByClass( reprIt->second->GetClassName() ); 182 | if (!representationCopy) 183 | { 184 | vtkErrorMacro("DeepCopy: Unable to construct representation type class '" << reprIt->second->GetClassName() << "'"); 185 | continue; 186 | } 187 | representationCopy->DeepCopy(reprIt->second); 188 | this->AddRepresentation(reprIt->first, representationCopy); 189 | representationCopy->Delete(); // this representation is now owned by the segment 190 | representationNamesToKeep.insert(reprIt->first); 191 | } 192 | 193 | // Remove representations that are not in the source segment 194 | for (reprIt = this->Representations.begin(); reprIt != this->Representations.end(); 195 | /*upon deletion the increment is done already, so don't increment here*/) 196 | { 197 | if (representationNamesToKeep.find(reprIt->first) == representationNamesToKeep.end()) 198 | { 199 | // this representation should not be kept 200 | RepresentationMap::iterator reprItToRemove = reprIt; 201 | ++reprIt; 202 | this->Representations.erase(reprItToRemove); 203 | continue; 204 | } 205 | ++reprIt; 206 | } 207 | } 208 | 209 | //---------------------------------------------------------------------------- 210 | void vtkSegment::DeepCopyMetadata(vtkSegment* source) 211 | { 212 | if (!source) 213 | { 214 | vtkErrorMacro("vtkSegment::DeepCopy failed: sourceSegment is invalid"); 215 | return; 216 | } 217 | 218 | // Copy properties 219 | this->SetName(source->Name); 220 | this->SetColor(source->Color); 221 | this->SetLabelValue(source->LabelValue); 222 | this->Tags = source->Tags; 223 | } 224 | 225 | 226 | //--------------------------------------------------------------------------- 227 | // (Xmin, Xmax, Ymin, Ymax, Zmin, Zmax) 228 | //--------------------------------------------------------------------------- 229 | void vtkSegment::GetBounds(double bounds[6]) 230 | { 231 | vtkBoundingBox boundingBox; 232 | 233 | RepresentationMap::iterator reprIt; 234 | for (reprIt=this->Representations.begin(); reprIt!=this->Representations.end(); ++reprIt) 235 | { 236 | vtkDataSet* representationDataSet = vtkDataSet::SafeDownCast(reprIt->second); 237 | if (representationDataSet) 238 | { 239 | double representationBounds[6] = { 1, -1, 1, -1, 1, -1 }; 240 | representationDataSet->GetBounds(representationBounds); 241 | boundingBox.AddBounds(representationBounds); 242 | } 243 | } 244 | boundingBox.GetBounds(bounds); 245 | } 246 | 247 | //--------------------------------------------------------------------------- 248 | vtkDataObject* vtkSegment::GetRepresentation(std::string name) 249 | { 250 | // Use find function instead of operator[] not to create empty representation if it is missing 251 | RepresentationMap::iterator reprIt = this->Representations.find(name); 252 | if (reprIt != this->Representations.end()) 253 | { 254 | return reprIt->second.GetPointer(); 255 | } 256 | else 257 | { 258 | return nullptr; 259 | } 260 | } 261 | 262 | //--------------------------------------------------------------------------- 263 | bool vtkSegment::AddRepresentation(std::string name, vtkDataObject* representation) 264 | { 265 | if (this->GetRepresentation(name) == representation) 266 | { 267 | return false; 268 | } 269 | this->Representations[name] = representation; // Representations stores the pointer in a smart pointer, which makes sure the object is not deleted 270 | this->Modified(); 271 | return true; 272 | } 273 | 274 | //--------------------------------------------------------------------------- 275 | bool vtkSegment::RemoveRepresentation(std::string name) 276 | { 277 | vtkDataObject* representation = this->GetRepresentation(name); 278 | if (!representation) 279 | { 280 | return false; 281 | } 282 | this->Representations.erase(name); 283 | this->Modified(); 284 | return true; 285 | } 286 | 287 | //--------------------------------------------------------------------------- 288 | void vtkSegment::RemoveAllRepresentations(std::string exceptionRepresentationName/*=""*/) 289 | { 290 | bool modified = false; 291 | RepresentationMap::iterator reprIt = this->Representations.begin(); 292 | while (reprIt != this->Representations.end()) 293 | { 294 | if (reprIt->first.compare(exceptionRepresentationName)) 295 | { 296 | // reprIt++ is safe, as iterators remain valid after erasing from a map 297 | this->Representations.erase(reprIt++); 298 | modified = true; 299 | } 300 | else 301 | { 302 | ++reprIt; 303 | } 304 | } 305 | if (modified) 306 | { 307 | this->Modified(); 308 | } 309 | } 310 | 311 | //--------------------------------------------------------------------------- 312 | void vtkSegment::GetContainedRepresentationNames(std::vector& representationNames) 313 | { 314 | representationNames.clear(); 315 | 316 | RepresentationMap::iterator reprIt; 317 | for (reprIt=this->Representations.begin(); reprIt!=this->Representations.end(); ++reprIt) 318 | { 319 | representationNames.push_back(reprIt->first); 320 | } 321 | } 322 | 323 | //--------------------------------------------------------------------------- 324 | void vtkSegment::SetTag(std::string tag, std::string value) 325 | { 326 | if (this->HasTag(tag) && !this->Tags[tag].compare(value)) 327 | { 328 | return; 329 | } 330 | 331 | this->Tags[tag] = value; 332 | this->Modified(); 333 | } 334 | 335 | //--------------------------------------------------------------------------- 336 | void vtkSegment::SetTag(std::string tag, int value) 337 | { 338 | std::stringstream ss; 339 | ss << value; 340 | this->SetTag(tag, ss.str()); 341 | } 342 | 343 | //--------------------------------------------------------------------------- 344 | void vtkSegment::RemoveTag(std::string tag) 345 | { 346 | this->Tags.erase(tag); 347 | this->Modified(); 348 | } 349 | 350 | //--------------------------------------------------------------------------- 351 | bool vtkSegment::GetTag(std::string tag, std::string &value) 352 | { 353 | std::map::iterator tagIt = this->Tags.find(tag); 354 | if (tagIt == this->Tags.end()) 355 | { 356 | return false; 357 | } 358 | 359 | value = tagIt->second; 360 | return true; 361 | } 362 | 363 | //--------------------------------------------------------------------------- 364 | bool vtkSegment::HasTag(std::string tag) 365 | { 366 | std::string value; 367 | return this->GetTag(tag, value); 368 | } 369 | 370 | //--------------------------------------------------------------------------- 371 | void vtkSegment::GetTags(std::map &tags) 372 | { 373 | tags = this->Tags; 374 | } 375 | -------------------------------------------------------------------------------- /src/vtkSegment.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkSegment_h 21 | #define __vtkSegment_h 22 | 23 | // VTK includes 24 | #include 25 | #include 26 | #include 27 | 28 | // STD includes 29 | #include 30 | #include 31 | 32 | // Segmentation includes 33 | #include "PolySegConfigure.h" 34 | 35 | /// \ingroup SegmentationCore 36 | /// \brief This class encapsulates a segment that is part of a segmentation 37 | /// \details 38 | /// A \sa vtkSegmentation can contain multiple segments (this class) each of which represent 39 | /// one anatomical or other structure (in labelmap terms, a "label"). Each segmentation can 40 | /// contain the structure in multiple representations. 41 | /// Default representation types include Binary labelmap and Closed surface, but additional 42 | /// custom representations can be added (see description of \sa vtkSegmentation). 43 | /// 44 | class PolySeg_EXPORT vtkSegment : public vtkObject 45 | { 46 | typedef std::map > RepresentationMap; 47 | 48 | public: 49 | 50 | static const double SEGMENT_COLOR_INVALID[3]; 51 | 52 | static const char* GetTerminologyEntryTagName(); 53 | 54 | static vtkSegment* New(); 55 | vtkTypeMacro(vtkSegment, vtkObject); 56 | void PrintSelf(ostream& os, vtkIndent indent) override; 57 | 58 | /// Set attributes from name/value pairs 59 | virtual void ReadXMLAttributes(const char** atts); 60 | 61 | /// Write this object's information to a MRML file in XML format. 62 | void WriteXML(ostream& of, int nIndent); 63 | 64 | /// Deep copy one segment into another 65 | virtual void DeepCopy(vtkSegment* source); 66 | 67 | /// Deep copy metadata (i.e., all data but representations) one segment into another 68 | virtual void DeepCopyMetadata(vtkSegment* source); 69 | 70 | /// Get bounding box in global RAS in the form (xmin,xmax, ymin,ymax, zmin,zmax). 71 | /// For image data bound is computed for the voxel corner points (not voxel center points). 72 | virtual void GetBounds(double bounds[6]); 73 | 74 | /// Get representation of a given type. This class is not responsible for conversion, only storage! 75 | /// \param name Representation name. Default representation names can be queried from \sa vtkSegmentationConverter, 76 | /// for example by calling vtkSegmentationConverter::GetSegmentationBinaryLabelmapRepresentationName() 77 | /// \return The specified representation object, nullptr if not present 78 | vtkDataObject* GetRepresentation(std::string name); 79 | 80 | /// Add representation 81 | /// \return True if the representation is changed. 82 | bool AddRepresentation(std::string type, vtkDataObject* representation); 83 | 84 | /// Remove representation of given type. 85 | /// \return True if there was a representation that was removed. 86 | bool RemoveRepresentation(std::string name); 87 | 88 | /// Remove all representations except one if specified. Fires only one Modified event 89 | /// \param exceptionRepresentationName Exception name that will not be removed 90 | /// (e.g. invalidate non-master representations), empty by default 91 | void RemoveAllRepresentations(std::string exceptionRepresentationName=""); 92 | 93 | /// Set/add tag 94 | void SetTag(std::string tag, std::string value); 95 | /// Set/add integer tag 96 | void SetTag(std::string tag, int value); 97 | 98 | /// Remove tag 99 | void RemoveTag(std::string tag); 100 | 101 | /// Get tag 102 | /// \param tag Name of requested tag 103 | /// \param value Output argument for the value of the tag if found 104 | /// \return True if tag is found, false otherwise 105 | bool GetTag(std::string tag, std::string &value); 106 | /// Determine if a tag is present 107 | bool HasTag(std::string tag); 108 | /// Get tags 109 | void GetTags(std::map &tags); 110 | 111 | /// Get representation names present in this segment in an output string vector 112 | void GetContainedRepresentationNames(std::vector& representationNames); 113 | 114 | public: 115 | /// Name (e.g. segment label in DICOM Segmentation Object) 116 | /// This is the default identifier of the segment within segmentation, so needs to be unique within a segmentation 117 | vtkGetStringMacro(Name); 118 | vtkSetStringMacro(Name); 119 | 120 | /// Color 121 | /// The actual color the segment is shown in. Can be overridden in the display node to allow displaying segment in 122 | /// selected views in a different color 123 | vtkGetVector3Macro(Color, double); 124 | vtkSetVector3Macro(Color, double); 125 | 126 | /// Flag indicating whether name was automatically generated. False after user manually overrides. True by default 127 | vtkGetMacro(NameAutoGenerated, bool); 128 | vtkSetMacro(NameAutoGenerated, bool); 129 | vtkBooleanMacro(NameAutoGenerated, bool); 130 | 131 | /// Flag indicating whether color was automatically generated. False after user manually overrides. True by default 132 | vtkGetMacro(ColorAutoGenerated, bool); 133 | vtkSetMacro(ColorAutoGenerated, bool); 134 | vtkBooleanMacro(ColorAutoGenerated, bool); 135 | 136 | /// Value in the binary labelmap that is used to represent the segment. 137 | vtkGetMacro(LabelValue, int); 138 | vtkSetMacro(LabelValue, int); 139 | 140 | protected: 141 | vtkSegment(); 142 | ~vtkSegment() override; 143 | void operator=(const vtkSegment&); 144 | 145 | protected: 146 | /// Stored representations. Map from type string to data object 147 | RepresentationMap Representations; 148 | char* Name; 149 | double Color[3]; 150 | /// Tags (for grouping and selection) 151 | std::map Tags; 152 | bool NameAutoGenerated; 153 | bool ColorAutoGenerated; 154 | int LabelValue; 155 | }; 156 | 157 | #endif // __vtkSegment_h 158 | -------------------------------------------------------------------------------- /src/vtkSegmentationConverter.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkSegmentationConverter_h 21 | #define __vtkSegmentationConverter_h 22 | 23 | // VTK includes 24 | #include 25 | #include 26 | 27 | // STD includes 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | // Segmentation includes 34 | #include "PolySegConfigure.h" 35 | 36 | #include "vtkSegmentationConverterRule.h" 37 | 38 | class vtkAbstractTransform; 39 | class vtkSegment; 40 | class vtkMatrix4x4; 41 | class vtkImageData; 42 | class vtkOrientedImageData; 43 | 44 | /// \ingroup SegmentationCore 45 | /// \brief Class that can convert between different representations of a segment. 46 | class PolySeg_EXPORT vtkSegmentationConverter : public vtkObject 47 | { 48 | public: 49 | typedef std::vector< vtkSmartPointer > ConverterRulesListType; 50 | 51 | typedef std::vector ConversionPathType; // Contains a list of converter rule names 52 | typedef std::pair ConversionPathAndCostType; 53 | typedef std::vector ConversionPathAndCostListType; 54 | 55 | /// Default representation types 56 | /// In binary and fractional labelmaps values <=0 are considered background voxels (outside), values>0 are foreground (inside). 57 | static const char* GetSegmentationBinaryLabelmapRepresentationName() { return "Binary labelmap"; }; 58 | static const char* GetSegmentationFractionalLabelmapRepresentationName() { return "Fractional labelmap"; }; 59 | static const char* GetSegmentationPlanarContourRepresentationName() { return "Planar contour"; }; 60 | static const char* GetSegmentationClosedSurfaceRepresentationName() { return "Closed surface"; }; 61 | static const char* GetBinaryLabelmapRepresentationName() { return GetSegmentationBinaryLabelmapRepresentationName(); }; 62 | static const char* GetFractionalLabelmapRepresentationName() { return GetSegmentationFractionalLabelmapRepresentationName(); }; 63 | static const char* GetPlanarContourRepresentationName() { return GetSegmentationPlanarContourRepresentationName(); }; 64 | static const char* GetClosedSurfaceRepresentationName() { return GetSegmentationClosedSurfaceRepresentationName(); }; 65 | 66 | // Common conversion parameters 67 | // ---------------------------- 68 | /// Reference image geometry conversion parameter 69 | /// Contains serialized matrix and extent 70 | static const std::string GetReferenceImageGeometryParameterName() { return "Reference image geometry"; }; 71 | 72 | /// Field names for 2D display parameters 73 | static const char* GetScalarRangeFieldName() {return "ScalarRange";}; 74 | static const char* GetThresholdValueFieldName() {return "ThresholdValue";}; 75 | static const char* GetInterpolationTypeFieldName() {return "InterpolationType";}; 76 | 77 | public: 78 | static vtkSegmentationConverter* New(); 79 | vtkTypeMacro(vtkSegmentationConverter, vtkObject); 80 | void PrintSelf(ostream& os, vtkIndent indent) override; 81 | 82 | /// Deep copy one converter into another 83 | virtual void DeepCopy(vtkSegmentationConverter* aConverter); 84 | 85 | /// Get all representations supported by the converter 86 | void GetAvailableRepresentationNames(std::set& representationNames); 87 | 88 | /// Get all possible conversions between two representations 89 | void GetPossibleConversions(const std::string& sourceRepresentationName, const std::string& targetRepresentationName, ConversionPathAndCostListType &pathsCosts); 90 | 91 | /// Get all conversion parameters used by the selected conversion path 92 | void GetConversionParametersForPath(vtkSegmentationConverterRule::ConversionParameterListType& conversionParameters, const ConversionPathType& path); 93 | 94 | /// Get all conversion parameters in this converter. Aggregates all parameters from all rules 95 | void GetAllConversionParameters(vtkSegmentationConverterRule::ConversionParameterListType& conversionParameters); 96 | 97 | /// Set a list of conversion parameters to all rules (cannot change the description, only the value) 98 | void SetConversionParameters(vtkSegmentationConverterRule::ConversionParameterListType parameters); 99 | 100 | /// Set a conversion parameter to all rules having this parameter 101 | void SetConversionParameter(const std::string& name, const std::string& value, const std::string& description=""); 102 | 103 | /// Get a conversion parameter value from first rule containing this parameter 104 | /// Note: all parameters with the same name should contain the same value 105 | std::string GetConversionParameter(const std::string& name); 106 | 107 | /// Get a conversion parameter description from first rule containing this parameter 108 | /// Note: all parameters with the same name should contain the same value 109 | std::string GetConversionParameterDescription(const std::string& description); 110 | 111 | /// Serialize all conversion parameters. 112 | /// The resulting string can be parsed in a segmentation converter object using /sa DeserializeConversionParameters 113 | std::string SerializeAllConversionParameters(); 114 | 115 | /// Parse conversion parameters in string and set it to the converter 116 | /// Such a string can be constructed in a segmentation converter object using /sa SerializeAllConversionParameters 117 | void DeserializeConversionParameters(std::string conversionParametersString); 118 | 119 | /// Apply a transform on the reference image geometry 120 | /// Linear: simply multiply the geometry matrix with the applied matrix, extent stays the same 121 | /// Non-linear: calculate new extents and change only the extents 122 | void ApplyTransformOnReferenceImageGeometry(vtkAbstractTransform* transform); 123 | 124 | // Utility functions 125 | public: 126 | /// Return cheapest path from a list of paths with costs 127 | static ConversionPathType GetCheapestPath(const ConversionPathAndCostListType &pathsCosts); 128 | 129 | /// Utility function for serializing geometry of oriented image data 130 | static std::string SerializeImageGeometry(vtkOrientedImageData* orientedImageData); 131 | 132 | /// Utility function for serializing geometry of a complete geometry matrix and regular image data (providing only extent) 133 | static std::string SerializeImageGeometry(vtkMatrix4x4* geometryMatrix, vtkImageData* imageData); 134 | 135 | /// Utility function for serializing geometry of a complete geometry matrix and given extents 136 | static std::string SerializeImageGeometry(vtkMatrix4x4* geometryMatrix, int extent[6]); 137 | 138 | /// Utility function for de-serializing reference image geometry into a dummy oriented image data 139 | /// \param geometryString String containing the serialized image geometry 140 | /// \param orientedImageData Dummy oriented image data containing the de-serialized geometry information 141 | /// \param allocateScalars Reallocate scalar array to match the new geometry. Can be set to false if image buffer is not used (e.g., an image is only created to parse image geometry) 142 | /// \param scalarType If specified then scalar type will be set to the requested value, otherwise current value will be kept. 143 | /// \param numberOfScalarsComponents If specified then the number of scalar components will be set to the requested value, otherwise current value will be kept. 144 | /// \return Success flag 145 | static bool DeserializeImageGeometry(std::string geometryString, vtkOrientedImageData* orientedImageData, bool allocateScalars=true, int scalarType=VTK_VOID, int numberOfScalarsComponents=-1); 146 | 147 | /// Utility function for de-serializing reference image geometry into a matrix and a dimensions vector 148 | /// \param geometryString String containing the serialized image geometry 149 | /// \param geometryMatrix Matrix containing the de-serialized directions, scaling and origin 150 | /// \param extent Vector containing the de-serialized extent 151 | /// \return Success flag 152 | static bool DeserializeImageGeometry(std::string geometryString, vtkMatrix4x4* geometryMatrix, int extent[6]); 153 | 154 | protected: 155 | /// Build a graph from ConverterRules list to facilitate faster finding of rules from a specific representation 156 | void RebuildRulesGraph(); 157 | 158 | /// Find a transform path between the specified coordinate frames. 159 | /// \param sourceRepresentationName representation to convert from 160 | /// \param targetRepresentationName representation to convert to 161 | /// \param conversionList Stores the list of converter rules to get from source to the target 162 | /// representation (if not found then returns with empty list). The caller should pass an 163 | /// empty list (when the method is called recursively the list is not empty). 164 | /// \param skipRepresentations Representations that should be ignored (e.g., because they are 165 | /// used already). The caller should pass an empty set (when the method is called recursively 166 | /// the set is not empty). 167 | void FindPath(const std::string& sourceRepresentationName, const std::string& targetRepresentationName, ConversionPathAndCostListType &pathsCosts, std::set& skipRepresentations); 168 | 169 | protected: 170 | vtkSegmentationConverter(); 171 | ~vtkSegmentationConverter() override; 172 | void operator=(const vtkSegmentationConverter&); 173 | 174 | protected: 175 | /// Converter rules. When the class is created it contains just the default converter rules but then 176 | /// rules may be customized with parameters and may store segment-specific information. 177 | /// Therefore, the rules should not be reused in other segments. 178 | ConverterRulesListType ConverterRules; 179 | 180 | /// For each "to" representation (first) stores a rule (second) 181 | typedef std::vector RulesListType; 182 | /// For each "from" representation (first) stores an array of rules (second) 183 | typedef std::map RepresentationToRepresentationToRuleMapType; 184 | 185 | /// Source representation to target representation rule graph 186 | RepresentationToRepresentationToRuleMapType RulesGraph; 187 | }; 188 | 189 | #endif // __vtkSegmentationConverter_h 190 | -------------------------------------------------------------------------------- /src/vtkSegmentationConverterFactory.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Andras Lasso, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #include "vtkSegmentationConverterFactory.h" 21 | 22 | // VTK includes 23 | #include 24 | #include 25 | 26 | // SegmentationCore includes 27 | #include "vtkSegmentationConverterRule.h" 28 | 29 | //---------------------------------------------------------------------------- 30 | // The segmentation converter rule manager singleton. 31 | // This MUST be default initialized to zero by the compiler and is 32 | // therefore not initialized here. The ClassInitialize and ClassFinalize methods handle this instance. 33 | static vtkSegmentationConverterFactory* vtkSegmentationConverterFactoryInstance; 34 | 35 | //---------------------------------------------------------------------------- 36 | // Must NOT be initialized. Default initialization to zero is necessary. 37 | unsigned int vtkSegmentationConverterFactoryInitialize::Count; 38 | 39 | //---------------------------------------------------------------------------- 40 | // Implementation of vtkSegmentationConverterFactoryInitialize class. 41 | //---------------------------------------------------------------------------- 42 | vtkSegmentationConverterFactoryInitialize::vtkSegmentationConverterFactoryInitialize() 43 | { 44 | if(++Self::Count == 1) 45 | { 46 | vtkSegmentationConverterFactory::classInitialize(); 47 | } 48 | } 49 | 50 | //---------------------------------------------------------------------------- 51 | vtkSegmentationConverterFactoryInitialize::~vtkSegmentationConverterFactoryInitialize() 52 | { 53 | if(--Self::Count == 0) 54 | { 55 | vtkSegmentationConverterFactory::classFinalize(); 56 | } 57 | } 58 | 59 | //---------------------------------------------------------------------------- 60 | 61 | //---------------------------------------------------------------------------- 62 | // Up the reference count so it behaves like New 63 | vtkSegmentationConverterFactory* vtkSegmentationConverterFactory::New() 64 | { 65 | vtkSegmentationConverterFactory* ret = vtkSegmentationConverterFactory::GetInstance(); 66 | ret->Register(nullptr); 67 | return ret; 68 | } 69 | 70 | //---------------------------------------------------------------------------- 71 | // Return the single instance of the vtkSegmentationConverterFactory 72 | vtkSegmentationConverterFactory* vtkSegmentationConverterFactory::GetInstance() 73 | { 74 | if(!vtkSegmentationConverterFactoryInstance) 75 | { 76 | // Try the factory first 77 | vtkSegmentationConverterFactoryInstance = (vtkSegmentationConverterFactory*)vtkObjectFactory::CreateInstance("vtkSegmentationConverterFactory"); 78 | // if the factory did not provide one, then create it here 79 | if(!vtkSegmentationConverterFactoryInstance) 80 | { 81 | vtkSegmentationConverterFactoryInstance = new vtkSegmentationConverterFactory; 82 | #ifdef VTK_HAS_INITIALIZE_OBJECT_BASE 83 | vtkSegmentationConverterFactoryInstance->InitializeObjectBase(); 84 | #endif 85 | } 86 | } 87 | // return the instance 88 | return vtkSegmentationConverterFactoryInstance; 89 | } 90 | 91 | //---------------------------------------------------------------------------- 92 | vtkSegmentationConverterFactory::vtkSegmentationConverterFactory() 93 | = default; 94 | 95 | //---------------------------------------------------------------------------- 96 | vtkSegmentationConverterFactory::~vtkSegmentationConverterFactory() 97 | = default; 98 | 99 | //---------------------------------------------------------------------------- 100 | void vtkSegmentationConverterFactory::PrintSelf(ostream& os, vtkIndent indent) 101 | { 102 | this->vtkObject::PrintSelf(os, indent); 103 | } 104 | 105 | //---------------------------------------------------------------------------- 106 | void vtkSegmentationConverterFactory::classInitialize() 107 | { 108 | // Allocate the singleton 109 | vtkSegmentationConverterFactoryInstance = vtkSegmentationConverterFactory::GetInstance(); 110 | } 111 | 112 | //---------------------------------------------------------------------------- 113 | void vtkSegmentationConverterFactory::classFinalize() 114 | { 115 | vtkSegmentationConverterFactoryInstance->Delete(); 116 | vtkSegmentationConverterFactoryInstance = nullptr; 117 | } 118 | 119 | //---------------------------------------------------------------------------- 120 | void vtkSegmentationConverterFactory::RegisterConverterRule(vtkSegmentationConverterRule* rule) 121 | { 122 | if (!rule) 123 | { 124 | vtkErrorMacro("RegisterConverterRule failed: invalid input rule"); 125 | return; 126 | } 127 | 128 | this->Rules.push_back(rule); 129 | } 130 | 131 | //---------------------------------------------------------------------------- 132 | void vtkSegmentationConverterFactory::UnregisterConverterRule(vtkSegmentationConverterRule* rule) 133 | { 134 | for (RuleListType::iterator ruleIt = this->Rules.begin(); ruleIt != this->Rules.end(); ++ruleIt) 135 | { 136 | if (ruleIt->GetPointer() == rule) 137 | { 138 | // Found 139 | this->Rules.erase(ruleIt); 140 | return; 141 | } 142 | } 143 | vtkWarningMacro("UnregisterConverterRule failed: rule not found"); 144 | } 145 | 146 | //---------------------------------------------------------------------------- 147 | void vtkSegmentationConverterFactory::CopyConverterRules(RuleListType &rules) 148 | { 149 | rules.clear(); 150 | for (RuleListType::iterator ruleIt = this->Rules.begin(); ruleIt != this->Rules.end(); ++ruleIt) 151 | { 152 | vtkSmartPointer rule = vtkSmartPointer::Take((*ruleIt)->Clone()); 153 | rules.push_back(rule); 154 | } 155 | } 156 | 157 | //---------------------------------------------------------------------------- 158 | const vtkSegmentationConverterFactory::RuleListType& vtkSegmentationConverterFactory::GetConverterRules() 159 | { 160 | return this->Rules; 161 | } 162 | 163 | //---------------------------------------------------------------------------- 164 | bool vtkSegmentationConverterFactory::DisableConverterRule(std::string sourceRepresentationName, std::string targetRepresentationName) 165 | { 166 | bool result = false; 167 | RuleListType rulesCopy = this->Rules; 168 | for (RuleListType::iterator ruleIt = rulesCopy.begin(); ruleIt != rulesCopy.end(); ++ruleIt) 169 | { 170 | if ( !sourceRepresentationName.compare(ruleIt->GetPointer()->GetSourceRepresentationName()) 171 | && !targetRepresentationName.compare(ruleIt->GetPointer()->GetTargetRepresentationName()) ) 172 | { 173 | this->UnregisterConverterRule(ruleIt->GetPointer()); 174 | result = true; 175 | } 176 | } 177 | return result; 178 | } 179 | 180 | //---------------------------------------------------------------------------- 181 | void vtkSegmentationConverterFactory::DisableRepresentation(std::string representationName) 182 | { 183 | RuleListType rulesCopy = this->Rules; 184 | for (RuleListType::iterator ruleIt = rulesCopy.begin(); ruleIt != rulesCopy.end(); ++ruleIt) 185 | { 186 | if ( !representationName.compare(ruleIt->GetPointer()->GetSourceRepresentationName()) 187 | || !representationName.compare(ruleIt->GetPointer()->GetTargetRepresentationName()) ) 188 | { 189 | this->UnregisterConverterRule(ruleIt->GetPointer()); 190 | } 191 | } 192 | } 193 | 194 | //---------------------------------------------------------------------------- 195 | vtkDataObject* vtkSegmentationConverterFactory::ConstructRepresentationObjectByClass(std::string className) 196 | { 197 | for (RuleListType::iterator ruleIt = this->Rules.begin(); ruleIt != this->Rules.end(); ++ruleIt) 198 | { 199 | vtkDataObject* representationObject = (*ruleIt)->ConstructRepresentationObjectByClass(className); 200 | if (representationObject) 201 | { 202 | return representationObject; 203 | } 204 | } 205 | 206 | // None of the registered rules can instantiate this type 207 | return nullptr; 208 | } 209 | 210 | //---------------------------------------------------------------------------- 211 | vtkDataObject* vtkSegmentationConverterFactory::ConstructRepresentationObjectByRepresentation(std::string representationName) 212 | { 213 | for (RuleListType::iterator ruleIt = this->Rules.begin(); ruleIt != this->Rules.end(); ++ruleIt) 214 | { 215 | vtkDataObject* representationObject = (*ruleIt)->ConstructRepresentationObjectByRepresentation(representationName); 216 | if (representationObject) 217 | { 218 | return representationObject; 219 | } 220 | } 221 | 222 | // None of the registered rules can instantiate this type 223 | return nullptr; 224 | } 225 | -------------------------------------------------------------------------------- /src/vtkSegmentationConverterFactory.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Andras Lasso, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkSegmentationConverterFactory_h 21 | #define __vtkSegmentationConverterFactory_h 22 | 23 | #include "PolySegConfigure.h" 24 | 25 | // VTK includes 26 | #include 27 | #include 28 | 29 | // STD includes 30 | #include 31 | 32 | class vtkSegmentationConverterRule; 33 | class vtkDataObject; 34 | 35 | /// \ingroup SegmentationCore 36 | /// \brief Class that can create vtkSegmentationConverter instances. 37 | /// 38 | /// This singleton class is a repository of all segmentation converter rules. 39 | /// Singleton pattern adopted from vtkEventBroker class. 40 | class PolySeg_EXPORT vtkSegmentationConverterFactory : public vtkObject 41 | { 42 | public: 43 | typedef std::vector< vtkSmartPointer > RuleListType; 44 | 45 | vtkTypeMacro(vtkSegmentationConverterFactory, vtkObject); 46 | void PrintSelf(ostream& os, vtkIndent indent) override; 47 | 48 | /// Create a copy of all registered converter rules. 49 | /// The rule argument is overwritten (any previous content is cleared) with rules 50 | /// copied from the list of all registered rules. 51 | void CopyConverterRules(RuleListType &rules); 52 | 53 | /// Add a converter rule. 54 | /// The factory (and all converter classes it creates) will keep a reference to this rule object, 55 | /// and it will not be deleted until all these referring classes are deleted. 56 | void RegisterConverterRule(vtkSegmentationConverterRule* rule); 57 | 58 | /// Remove a converter rule from the factory. 59 | /// This does not affect converters that have already been created. 60 | void UnregisterConverterRule(vtkSegmentationConverterRule* rule); 61 | 62 | /// Get all registered converter rules 63 | const RuleListType& GetConverterRules(); 64 | 65 | /// Disable a converter rule, preventing it from being used. Achieved by unregistering the rule 66 | /// that can create the representation. 67 | /// \return Success flag indicating whether a rule with the specified representations was found and disabled 68 | bool DisableConverterRule(std::string sourceRepresentationName, std::string targetRepresentationName); 69 | 70 | /// Disable a representation, preventing it from being created. Achieved by unregistering the rules 71 | /// that can create the representation. 72 | void DisableRepresentation(std::string representationName); 73 | 74 | /// Constructs representation object from class name using the ConstructRepresentationObject 75 | /// methods in the registered conversion rules that must be able to instantiate the representation 76 | /// classes they support. 77 | vtkDataObject* ConstructRepresentationObjectByClass(std::string className); 78 | 79 | /// Constructs representation object from representation name using the ConstructRepresentationObject 80 | /// methods in the registered conversion rules that must be able to instantiate the representation 81 | /// classes they support. 82 | vtkDataObject* ConstructRepresentationObjectByRepresentation(std::string representationName); 83 | 84 | public: 85 | /// Return the singleton instance with no reference counting. 86 | static vtkSegmentationConverterFactory* GetInstance(); 87 | 88 | /// This is a singleton pattern New. There will only be ONE 89 | /// reference to a vtkSegmentationConverterFactory object per process. Clients that 90 | /// call this must call Delete on the object so that the reference 91 | /// counting will work. The single instance will be unreferenced when 92 | /// the program exits. 93 | static vtkSegmentationConverterFactory* New(); 94 | 95 | protected: 96 | vtkSegmentationConverterFactory(); 97 | ~vtkSegmentationConverterFactory() override; 98 | vtkSegmentationConverterFactory(const vtkSegmentationConverterFactory&); 99 | void operator=(const vtkSegmentationConverterFactory&); 100 | 101 | // Singleton management functions. 102 | static void classInitialize(); 103 | static void classFinalize(); 104 | 105 | friend class vtkSegmentationConverterFactoryInitialize; 106 | typedef vtkSegmentationConverterFactory Self; 107 | 108 | /// Registered converter rules 109 | RuleListType Rules; 110 | }; 111 | 112 | class PolySeg_EXPORT vtkSegmentationConverterFactoryInitialize 113 | { 114 | public: 115 | typedef vtkSegmentationConverterFactoryInitialize Self; 116 | 117 | vtkSegmentationConverterFactoryInitialize(); 118 | ~vtkSegmentationConverterFactoryInitialize(); 119 | private: 120 | static unsigned int Count; 121 | }; 122 | 123 | /// This instance will show up in any translation unit that uses 124 | /// vtkSegmentationConverterFactory. It will make sure vtkSegmentationConverterFactory is initialized 125 | /// before it is used. 126 | static vtkSegmentationConverterFactoryInitialize vtkSegmentationConverterFactoryInitializer; 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /src/vtkSegmentationConverterRule.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | // Segmentations includes 21 | #include "vtkSegmentationConverterRule.h" 22 | 23 | // VTK includes 24 | #include 25 | #include 26 | 27 | // SegmentationCore includes 28 | #include 29 | 30 | //---------------------------------------------------------------------------- 31 | vtkSegmentationConverterRule::vtkSegmentationConverterRule() 32 | : ReplaceTargetRepresentation(false) 33 | { 34 | } 35 | 36 | //---------------------------------------------------------------------------- 37 | vtkSegmentationConverterRule::~vtkSegmentationConverterRule() 38 | { 39 | this->ConversionParameters.clear(); 40 | } 41 | 42 | //---------------------------------------------------------------------------- 43 | vtkSegmentationConverterRule* vtkSegmentationConverterRule::Clone() 44 | { 45 | vtkSegmentationConverterRule* clone = this->CreateRuleInstance(); 46 | clone->ConversionParameters = this->ConversionParameters; 47 | return clone; 48 | } 49 | 50 | //---------------------------------------------------------------------------- 51 | bool vtkSegmentationConverterRule::CreateTargetRepresentation(vtkSegment* segment) 52 | { 53 | // Get target representation 54 | vtkSmartPointer targetRepresentation = segment->GetRepresentation( 55 | this->GetTargetRepresentationName()); 56 | 57 | // Create an empty target representation if it does not exist, or if we want to replace the target 58 | if (!targetRepresentation.GetPointer() || this->ReplaceTargetRepresentation) 59 | { 60 | targetRepresentation = vtkSmartPointer::Take( 61 | this->ConstructRepresentationObjectByRepresentation(this->GetTargetRepresentationName())); 62 | segment->AddRepresentation(this->GetTargetRepresentationName(), targetRepresentation); 63 | } 64 | return true; 65 | } 66 | 67 | //---------------------------------------------------------------------------- 68 | void vtkSegmentationConverterRule::GetRuleConversionParameters(ConversionParameterListType& conversionParameters) 69 | { 70 | // Copy rule conversion parameters into aggregated path parameters 71 | ConversionParameterListType::iterator paramIt; 72 | for (paramIt = this->ConversionParameters.begin(); paramIt != this->ConversionParameters.end(); ++paramIt) 73 | { 74 | conversionParameters[paramIt->first] = paramIt->second; 75 | } 76 | } 77 | 78 | //---------------------------------------------------------------------------- 79 | void vtkSegmentationConverterRule::SetConversionParameter(const std::string& name, const std::string& value, const std::string& description/*=""*/) 80 | { 81 | this->ConversionParameters[name].first = value; 82 | 83 | if (!description.empty()) 84 | { 85 | this->ConversionParameters[name].second = description; 86 | } 87 | } 88 | 89 | //---------------------------------------------------------------------------- 90 | std::string vtkSegmentationConverterRule::GetConversionParameter(const std::string& name) 91 | { 92 | return this->ConversionParameters[name].first; 93 | } 94 | 95 | //---------------------------------------------------------------------------- 96 | std::string vtkSegmentationConverterRule::GetConversionParameterDescription(const std::string& name) 97 | { 98 | return this->ConversionParameters[name].second; 99 | } 100 | 101 | //---------------------------------------------------------------------------- 102 | bool vtkSegmentationConverterRule::HasConversionParameter(const std::string& name) 103 | { 104 | return (this->ConversionParameters.count(name) > 0); 105 | } 106 | -------------------------------------------------------------------------------- /src/vtkSegmentationConverterRule.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkSegmentationConverterRule_h 21 | #define __vtkSegmentationConverterRule_h 22 | 23 | #include "PolySegConfigure.h" 24 | 25 | // VTK includes 26 | #include 27 | 28 | // STD includes 29 | #include 30 | #include 31 | #include 32 | 33 | class vtkDataObject; 34 | class vtkSegmentation; 35 | class vtkSegment; 36 | 37 | /// Helper macro for supporting cloning of rules 38 | #ifndef vtkSegmentationConverterRuleNewMacro 39 | #define vtkSegmentationConverterRuleNewMacro(newClass) \ 40 | vtkStandardNewMacro(newClass); \ 41 | vtkSegmentationConverterRule* newClass::CreateRuleInstance() \ 42 | { \ 43 | return newClass::New(); \ 44 | } 45 | #endif 46 | 47 | /// \ingroup SegmentationCore 48 | /// \brief Abstract converter rule class. Subclasses perform conversions between specific 49 | /// representation types. They define source and target type and provide ways to create those 50 | /// types of objects. 51 | class PolySeg_EXPORT vtkSegmentationConverterRule : public vtkObject 52 | { 53 | public: 54 | /// Conversion parameter list type. Maps the conversion parameter name to a pair consisting of the 55 | /// value of the parameter (the default value if it is defined in the converter rule) and the 56 | /// description of the parameter that appears as tooltip in the conversion parameters widget 57 | /// ( name => (value, description) ) 58 | typedef std::map > ConversionParameterListType; 59 | 60 | /// Constant to use for converter rules with "infinite" computational cost (i.e. disabled) 61 | /// It's about UINT_MAX / 400 (allows us to have a few hundred disabled rules) 62 | static unsigned int GetConversionInfiniteCost() { return 10000000; }; 63 | 64 | public: 65 | //static vtkSegmentationConverterRule* New(); 66 | vtkTypeMacro(vtkSegmentationConverterRule, vtkObject); 67 | 68 | /// Create instance of the default node. Similar to New but virtual method. 69 | /// Subclasses should implement this method by 70 | virtual vtkSegmentationConverterRule* CreateRuleInstance() = 0; 71 | 72 | /// Create a new instance of this rule and copy its contents 73 | virtual vtkSegmentationConverterRule* Clone(); 74 | 75 | /// Constructs representation object from representation name for the supported representation classes 76 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 77 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 78 | virtual vtkDataObject* ConstructRepresentationObjectByRepresentation(std::string representationName) = 0; 79 | 80 | /// Constructs representation object from class name for the supported representation classes 81 | /// (typically source and target representation VTK classes, subclasses of vtkDataObject) 82 | /// Note: Need to take ownership of the created object! For example using vtkSmartPointer::Take 83 | virtual vtkDataObject* ConstructRepresentationObjectByClass(std::string className) = 0; 84 | 85 | /// Perform pre-conversion steps across the specified segments in the segmentation 86 | /// This step should be unneccessary if only converting a single segment 87 | virtual bool PreConvert(vtkSegmentation* vtkNotUsed(segmentation)) { return true; }; 88 | 89 | /// Update the target representation based on the source representation 90 | /// Initializes the target representation and calls ConvertInternal 91 | /// \sa ConvertInternal 92 | virtual bool Convert(vtkSegment* segment) = 0; 93 | 94 | /// Perform post-conversion steps across the specified segments in the segmentation 95 | /// This step should be unneccessary if only converting a single segment 96 | virtual bool PostConvert(vtkSegmentation* vtkNotUsed(segmentation)) { return true; }; 97 | 98 | /// Get the cost of the conversion. 99 | /// \return Expected duration of the conversion in milliseconds. If the arguments are omitted, then a rough average can be 100 | /// given just to indicate the relative computational cost of the algorithm. If the objects are given, then a more educated 101 | /// guess can be made based on the object properties (dimensions, number of points, etc). 102 | virtual unsigned int GetConversionCost(vtkDataObject* sourceRepresentation=nullptr, vtkDataObject* targetRepresentation=nullptr) 103 | { 104 | (void)(sourceRepresentation); // unused 105 | (void)(targetRepresentation); // unused 106 | return 100; 107 | }; 108 | 109 | /// Human-readable name of the converter rule 110 | virtual const char* GetName() = 0; 111 | 112 | /// Human-readable name of the source representation 113 | virtual const char* GetSourceRepresentationName() = 0; 114 | 115 | /// Human-readable name of the target representation 116 | virtual const char* GetTargetRepresentationName() = 0; 117 | 118 | /// Get rule conversion parameters for aggregated path parameters. 119 | /// Existing values in the map are overwritten, missing name&values are added. 120 | virtual void GetRuleConversionParameters(ConversionParameterListType& conversionParameters); 121 | 122 | /// Set a conversion parameter 123 | virtual void SetConversionParameter(const std::string& name, const std::string& value, const std::string& description=""); 124 | 125 | /// Get a conversion parameter value 126 | virtual std::string GetConversionParameter(const std::string& name); 127 | 128 | /// Get a conversion parameter description 129 | virtual std::string GetConversionParameterDescription(const std::string& name); 130 | 131 | /// Determine if the rule has a parameter with a certain name 132 | bool HasConversionParameter(const std::string& name); 133 | 134 | protected: 135 | /// Update the target representation based on the source representation 136 | virtual bool CreateTargetRepresentation(vtkSegment* segment); 137 | 138 | vtkSegmentationConverterRule(); 139 | ~vtkSegmentationConverterRule() override; 140 | void operator=(const vtkSegmentationConverterRule&); 141 | 142 | protected: 143 | /// Dictionary of conversion parameters in form of name -> default value, description. 144 | /// Each conversion rule defines its required/possible conversion parameters, 145 | /// and sets possible default values whenever applicable. Required parameters have empty defaults. 146 | /// When the user changes the parameter value, then the default is being overwritten to contain the 147 | /// custom value, but for new segmentations, it is initially the default. 148 | ConversionParameterListType ConversionParameters; 149 | 150 | /// Used when calling createTargetRepresentation 151 | /// If true, replaces the target representation of the segment with a new object, even if one already exists 152 | /// If false, will only create a target representation if one already doesn't exist. 153 | /// False by default. 154 | bool ReplaceTargetRepresentation; 155 | 156 | friend class vtkSegmentationConverter; 157 | }; 158 | 159 | #endif // __vtkSegmentationConverterRule_h 160 | -------------------------------------------------------------------------------- /src/vtkSegmentationHistory.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #ifndef __vtkSegmentationHistory_h 21 | #define __vtkSegmentationHistory_h 22 | 23 | // VTK includes 24 | #include 25 | #include 26 | 27 | // STD includes 28 | #include 29 | #include 30 | #include 31 | 32 | #include "PolySegConfigure.h" 33 | 34 | class vtkCallbackCommand; 35 | class vtkSegment; 36 | class vtkSegmentation; 37 | 38 | /// \ingroup SegmentationCore 39 | class PolySeg_EXPORT vtkSegmentationHistory : public vtkObject 40 | { 41 | public: 42 | static vtkSegmentationHistory* New(); 43 | vtkTypeMacro(vtkSegmentationHistory, vtkObject); 44 | void PrintSelf(ostream& os, vtkIndent indent) override; 45 | 46 | /// Selects a segmentation that the states will be stored of. Current state of the segmentation is not stored. 47 | /// \param segmentation Segmentation to store. Deletes all stored states of the previously set segmentation. 48 | void SetSegmentation(vtkSegmentation* segmentation); 49 | 50 | /// Get segmentation that the states will be stored of. 51 | vtkGetMacro(Segmentation, vtkSegmentation*); 52 | 53 | /// Saves all master representations of the segmentation in its current state. 54 | /// States more recent than the last restored state are removed. 55 | /// \return Success flag 56 | bool SaveState(); 57 | 58 | /// Restores previous state of the segmentation. 59 | /// \return Success flag 60 | bool RestorePreviousState(); 61 | 62 | /// Check if it is possible to go back to a previous state (undo). 63 | /// \return True if the operation is allowed 64 | bool IsRestorePreviousStateAvailable(); 65 | 66 | /// Restores next state of the segmentation. 67 | /// \return Success flag 68 | bool RestoreNextState(); 69 | 70 | /// Check if it is possible to go restore the next state (redo). 71 | /// \return True if the operation is allowed 72 | bool IsRestoreNextStateAvailable(); 73 | 74 | /// Delete all states from memory 75 | void RemoveAllStates(); 76 | 77 | /// Limits how many states may be stored. 78 | /// If the number of stored states exceed the limit then the oldest state is removed. 79 | void SetMaximumNumberOfStates(unsigned int maximumNumberOfStates); 80 | 81 | /// Get the limit of how many states may be stored. 82 | vtkGetMacro(MaximumNumberOfStates, unsigned int); 83 | 84 | protected: 85 | /// Callback function called when the segmentation has been modified. 86 | /// It clears all states that are more recent than the last restored state. 87 | static void OnSegmentationModified(vtkObject* caller, unsigned long eid, void* clientData, void* callData); 88 | 89 | /// Delete all states that are more recent than the last restored state 90 | void RemoveAllNextStates(); 91 | 92 | /// Delete all old states so that we keep only up to MaximumNumberOfStates states 93 | void RemoveAllObsoleteStates(); 94 | 95 | /// Restores a state defined by stateIndex. 96 | bool RestoreState(unsigned int stateIndex); 97 | 98 | protected: 99 | vtkSegmentationHistory(); 100 | ~vtkSegmentationHistory() override; 101 | void operator=(const vtkSegmentationHistory&); 102 | 103 | /// Deep copies source segment to destination segment. If the same representation is found in baseline 104 | /// with up-to-date timestamp then the representation is reused from baseline. 105 | void CopySegment(vtkSegment* destination, vtkSegment* source, vtkSegment* baseline, std::vector representationsToIgnore); 106 | 107 | protected: /// Container type for segments. Maps segment IDs to segment objects 108 | typedef std::map > SegmentsMap; 109 | 110 | struct SegmentationState 111 | { 112 | SegmentsMap Segments; 113 | std::vector SegmentIds; // order of segments 114 | }; 115 | 116 | vtkSegmentation* Segmentation; 117 | vtkCallbackCommand* SegmentationModifiedCallbackCommand; 118 | std::deque SegmentationStates; 119 | unsigned int MaximumNumberOfStates; 120 | 121 | // Index of the state in SegmentationStates that was restored last. 122 | // If index == size of states then it means that the segmentation has changed 123 | // since the last restored state. 124 | unsigned int LastRestoredState; 125 | 126 | bool RestoreStateInProgress; 127 | }; 128 | 129 | #endif // __vtkSegmentation_h 130 | -------------------------------------------------------------------------------- /src/vtkTopologicalHierarchy.cxx: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | #include "vtkTopologicalHierarchy.h" 21 | 22 | // VTK includes 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | //---------------------------------------------------------------------------- 30 | vtkStandardNewMacro(vtkTopologicalHierarchy); 31 | 32 | //---------------------------------------------------------------------------- 33 | vtkTopologicalHierarchy::vtkTopologicalHierarchy() 34 | { 35 | this->InputPolyDataCollection = nullptr; 36 | vtkSmartPointer inputPolyData = vtkSmartPointer::New(); 37 | this->SetInputPolyDataCollection(inputPolyData); 38 | 39 | this->OutputLevels = nullptr; 40 | vtkSmartPointer outputLevels = vtkSmartPointer::New(); 41 | this->SetOutputLevels(outputLevels); 42 | 43 | this->ContainConstraintFactor = 0.0; 44 | 45 | this->MaximumLevel = 7; 46 | } 47 | 48 | //---------------------------------------------------------------------------- 49 | vtkTopologicalHierarchy::~vtkTopologicalHierarchy() 50 | { 51 | this->SetInputPolyDataCollection(nullptr); 52 | this->SetOutputLevels(nullptr); 53 | } 54 | 55 | //---------------------------------------------------------------------------- 56 | void vtkTopologicalHierarchy::PrintSelf(ostream& os, vtkIndent indent) 57 | { 58 | this->Superclass::PrintSelf(os, indent); 59 | } 60 | 61 | //---------------------------------------------------------------------------- 62 | vtkIntArray* vtkTopologicalHierarchy::GetOutputLevels() 63 | { 64 | return this->OutputLevels; 65 | } 66 | 67 | //---------------------------------------------------------------------------- 68 | bool vtkTopologicalHierarchy::Contains(vtkPolyData* polyOut, vtkPolyData* polyIn) 69 | { 70 | if (!polyIn || !polyOut) 71 | { 72 | vtkErrorMacro("Contains: Empty input parameters!"); 73 | return false; 74 | } 75 | 76 | double extentOut[6] = {0.0,0.0,0.0,0.0,0.0,0.0}; 77 | polyOut->GetBounds(extentOut); 78 | 79 | double extentIn[6] = {0.0,0.0,0.0,0.0,0.0,0.0}; 80 | polyIn->GetBounds(extentIn); 81 | 82 | if ( extentOut[0] < extentIn[0] - this->ContainConstraintFactor * (extentOut[1]-extentOut[0]) 83 | && extentOut[1] > extentIn[1] + this->ContainConstraintFactor * (extentOut[1]-extentOut[0]) 84 | && extentOut[2] < extentIn[2] - this->ContainConstraintFactor * (extentOut[3]-extentOut[2]) 85 | && extentOut[3] > extentIn[3] + this->ContainConstraintFactor * (extentOut[3]-extentOut[2]) 86 | && extentOut[4] < extentIn[4] - this->ContainConstraintFactor * (extentOut[5]-extentOut[4]) 87 | && extentOut[5] > extentIn[5] + this->ContainConstraintFactor * (extentOut[5]-extentOut[4]) ) 88 | { 89 | return true; 90 | } 91 | 92 | return false; 93 | } 94 | 95 | //---------------------------------------------------------------------------- 96 | void vtkTopologicalHierarchy::Update() 97 | { 98 | if (!this->InputPolyDataCollection || !this->OutputLevels) 99 | { 100 | vtkErrorMacro("Update: Input poly data collection and output int array have to be initialized!"); 101 | return; 102 | } 103 | 104 | this->OutputLevels->Initialize(); 105 | unsigned int numberOfPolyData = this->InputPolyDataCollection->GetNumberOfItems(); 106 | 107 | // Check input polydata collection 108 | for (unsigned int polyOutIndex=0; polyOutIndexInputPolyDataCollection->GetItemAsObject(polyOutIndex)); 111 | if (!polyOut) 112 | { 113 | vtkErrorMacro("Update: Input collection contains invalid object at item " << polyOutIndex); 114 | return; 115 | } 116 | } 117 | 118 | std::vector > containedPolyData(numberOfPolyData); 119 | this->OutputLevels->SetNumberOfComponents(1); 120 | this->OutputLevels->SetNumberOfTuples(numberOfPolyData); 121 | this->OutputLevels->FillComponent(0, -1); 122 | 123 | // Step 1: Set level of polydata containing no other polydata to 0 124 | this->InputPolyDataCollection->InitTraversal(); 125 | for (unsigned int polyOutIndex=0; polyOutIndexInputPolyDataCollection->GetItemAsObject(polyOutIndex)); 128 | 129 | for (unsigned int polyInIndex=0; polyInIndexInputPolyDataCollection->GetItemAsObject(polyInIndex)); 137 | 138 | if (this->Contains(polyOut, polyIn)) 139 | { 140 | containedPolyData[polyOutIndex].push_back(polyInIndex); 141 | } 142 | } 143 | 144 | if (containedPolyData[polyOutIndex].size() == 0) 145 | { 146 | this->OutputLevels->SetValue(polyOutIndex, 0); 147 | } 148 | } 149 | 150 | // Step 2: Set level of the polydata containing other polydata to one bigger than the highest contained level 151 | vtkSmartPointer outputLevelsSnapshot = vtkSmartPointer::New(); 152 | unsigned int currentLevel = 1; 153 | while (this->OutputContainsEmptyLevels() && currentLevel < this->MaximumLevel) 154 | { 155 | // Creating snapshot of the level array state so that the newly set values don't interfere with the check 156 | // Without this, the check "does all contained polydata have level values assigned" is corrupted 157 | outputLevelsSnapshot->DeepCopy(this->OutputLevels); 158 | 159 | // Step 3: For all polydata without level value assigned 160 | for (unsigned int polyOutIndex=0; polyOutIndexOutputLevels->GetValue(polyOutIndex) > -1) 163 | { 164 | continue; 165 | } 166 | 167 | // Step 4: If all contained polydata have level values assigned, then set it to the current level value 168 | // The level that is to be set cannot be lower than the current level value, because then we would 169 | // already have assigned it in the previous iterations. 170 | bool allContainedPolydataHasLevelValueAssigned = true; 171 | for (unsigned int polyInIndex = 0; 172 | polyInIndex < numberOfPolyData; 173 | ++polyInIndex) 174 | { 175 | if (polyOutIndex==polyInIndex) 176 | { 177 | continue; 178 | } 179 | bool isContained = false; 180 | for (std::vector::iterator it = containedPolyData[polyOutIndex].begin(); 181 | it != containedPolyData[polyOutIndex].end(); 182 | ++it) 183 | { 184 | if ((*it) == polyInIndex) 185 | { 186 | isContained = true; 187 | break; 188 | } 189 | } 190 | if (!isContained) 191 | { 192 | continue; 193 | } 194 | 195 | if (outputLevelsSnapshot->GetValue(polyInIndex) == -1) 196 | { 197 | allContainedPolydataHasLevelValueAssigned = false; 198 | break; 199 | } 200 | } 201 | if (allContainedPolydataHasLevelValueAssigned) 202 | { 203 | this->OutputLevels->SetValue(polyOutIndex, currentLevel); 204 | } 205 | } 206 | 207 | // Increase current level for the next iteration 208 | currentLevel++; 209 | } 210 | 211 | // Step 5: Set maximum level to all polydata that has no level value assigned 212 | for (unsigned int polyOutIndex=0; polyOutIndexOutputLevels->GetValue(polyOutIndex) == -1) 215 | { 216 | this->OutputLevels->SetValue(polyOutIndex, this->MaximumLevel); 217 | } 218 | } 219 | } 220 | 221 | //---------------------------------------------------------------------------- 222 | bool vtkTopologicalHierarchy::OutputContainsEmptyLevels() 223 | { 224 | if (!this->OutputLevels) 225 | { 226 | return false; 227 | } 228 | 229 | for (int i=0; iOutputLevels->GetNumberOfTuples(); ++i) 230 | { 231 | if (this->OutputLevels->GetValue(i) == -1) 232 | { 233 | return true; 234 | } 235 | } 236 | 237 | return false; 238 | } 239 | -------------------------------------------------------------------------------- /src/vtkTopologicalHierarchy.h: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | 3 | Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) 4 | Queen's University, Kingston, ON, Canada. All Rights Reserved. 5 | 6 | See https://opensource.org/licenses/BSD-2-Clause for details. 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | This file was originally developed by Csaba Pinter, PerkLab, Queen's University 15 | and was supported through the Applied Cancer Research Unit program of Cancer Care 16 | Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care 17 | 18 | ==============================================================================*/ 19 | 20 | // .NAME vtkTopologicalHierarchy - Assigns hierarchy level values to the elements of a poly data collection 21 | // .SECTION Description 22 | 23 | 24 | #ifndef __vtkTopologicalHierarchy_h 25 | #define __vtkTopologicalHierarchy_h 26 | 27 | // VTK includes 28 | #include 29 | 30 | #include "PolySegConfigure.h" 31 | 32 | class vtkIntArray; 33 | 34 | /// \ingroup PolySeg 35 | /// \brief Algorithm class for computing topological hierarchy of multiple poly data models. 36 | /// The levels of the models are determined according to the models they contain, an outer 37 | /// model always having larger level value than the inner ones. To determine whether a model 38 | /// contains another, their bounding boxes are considered. It is possible to constrain a gap 39 | /// or allow the inner model to protrude the surface of the outer one. The size of this gap 40 | /// or allowance is defined as a factor /sa ContainConstraintFactor of the outer model size. 41 | /// This algorithm can be used to automatically determine optimal opacities in complex scenes. 42 | class PolySeg_EXPORT vtkTopologicalHierarchy : public vtkObject 43 | { 44 | public: 45 | 46 | static vtkTopologicalHierarchy *New(); 47 | vtkTypeMacro(vtkTopologicalHierarchy, vtkObject ); 48 | void PrintSelf(ostream& os, vtkIndent indent) override; 49 | 50 | /// Get output topological hierarchy levels 51 | virtual vtkIntArray* GetOutputLevels(); 52 | 53 | /// Compute topological hierarchy levels for input poly data models using 54 | /// their bounding boxes. 55 | /// This function has to be explicitly called! 56 | /// Output can be get using GetOutputLevels() 57 | virtual void Update(); 58 | 59 | /// Set input poly data collection 60 | vtkSetObjectMacro(InputPolyDataCollection, vtkPolyDataCollection); 61 | 62 | /// Set constraint factor (used when determining if a poly data contains another) 63 | vtkSetMacro(ContainConstraintFactor, double); 64 | /// Get constraint factor (used when determining if a poly data contains another) 65 | vtkGetMacro(ContainConstraintFactor, double); 66 | 67 | protected: 68 | /// Set output topological hierarchy levels 69 | vtkSetObjectMacro(OutputLevels, vtkIntArray); 70 | 71 | protected: 72 | /// Determines if polyOut contains polyIn considering the constraint factor 73 | /// /sa ContainConstraintFactor 74 | bool Contains(vtkPolyData* polyOut, vtkPolyData* polyIn); 75 | 76 | /// Determines if there are empty entries in the output level array 77 | bool OutputContainsEmptyLevels(); 78 | 79 | protected: 80 | /// Collection of poly data to determine the hierarchy for 81 | vtkPolyDataCollection* InputPolyDataCollection; 82 | 83 | /// Array containing the topological hierarchy levels for the input poly data 84 | /// Update function needs to be called to compute the array 85 | /// The level values correspond to the poly data with the same index in the input collection 86 | vtkIntArray* OutputLevels; 87 | 88 | /// Constraint factor used when determining if a poly data contains another 89 | /// It defines a 'gap' that is needed between the outer and inner poly data. The gap is computed 90 | /// as this factor multiplied by the bounding box edge length at each dimension. 91 | /// In case of positive value, the inner poly data has to be that much smaller than the outer one 92 | /// In case of negative value, it is rather an allowance by which the inner polydata can reach 93 | /// outside the other 94 | double ContainConstraintFactor; 95 | 96 | /// Maximum level that can be assigned to a poly data 97 | unsigned int MaximumLevel; 98 | 99 | protected: 100 | vtkTopologicalHierarchy(); 101 | ~vtkTopologicalHierarchy() override; 102 | 103 | private: 104 | vtkTopologicalHierarchy(const vtkTopologicalHierarchy&) = delete; 105 | void operator=(const vtkTopologicalHierarchy&) = delete; 106 | }; 107 | 108 | #endif 109 | --------------------------------------------------------------------------------