├── 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 | 
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