├── Modules ├── Utils │ ├── CMakeLists.txt │ └── ssmUtils.h ├── Options │ ├── CMakeLists.txt │ ├── ssmModelSpecificityOptions.h │ ├── ssmModelCrossValidationOptions.h │ ├── ssmModelBuildingOptions.h │ ├── ssmAlignmentOptions.h │ ├── ssmExtractionOptions.h │ ├── ssmReferenceOptions.h │ ├── ssmCorrespondenceOptions.h │ └── ssmOptionsBase.h ├── SSM │ ├── ssmTypes.h │ ├── CMakeLists.txt │ ├── ssmMeshToLevelSetImageFilter.h │ ├── ssmShapeModelToImageRegistrationMethod.h │ ├── ssmBinaryImageToLevelSetImageFilter.h │ ├── ssmMeshPropertiesCalculator.h │ ├── ssmShapeModelToImageRegistrationMethod.hxx │ ├── ssmMeshToImageRegistrationMethod.h │ ├── ssmImage3DMeshSource.h │ ├── ssmMeshToLevelSetImageFilter.hxx │ ├── ssmShapeModelRegistrationMethodBase.h │ ├── ssmMeshPropertiesCalculator.hxx │ ├── ssmShapeModelMultiTransform.hxx │ ├── ssmShapeModelMultiTransform.h │ ├── ssmImage3DMeshSource.hxx │ ├── ssmShapeModelToLevelSetImageMetric.h │ ├── ssmPointSetToImageMetrics.h │ ├── ssmMeshToImageRegistrationMethod.hxx │ ├── ssmPointSetToPointSetMetrics.h │ ├── ssmShapeModelRegistrationMethodBase.hxx │ └── ssmInitializeSpatialTransform.h └── Configs │ └── config.ini ├── .gitignore ├── LICENSE ├── README.md ├── Applications ├── CMakeLists.txt ├── ssm-specificity.cxx ├── ssm-build-shapemodel.cxx ├── ssm-build-reference.cxx ├── ssm-cross-validation.cxx ├── ssm-extract-surface.cxx ├── ssm-fit-shapemodel-to-image.cxx └── ssm-fit-shapemodel-to-surface.cxx └── CMakeLists.txt /Modules/Utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Utils) 2 | 3 | set(HEADER 4 | ssmUtils.h 5 | ) 6 | 7 | add_custom_target(Utils ALL SOURCES ${HEADER}) 8 | -------------------------------------------------------------------------------- /Modules/Options/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Options) 2 | 3 | set(HEADER 4 | ssmOptionsBase.h 5 | ssmExtractionOptions.h 6 | ssmAlignmentOptions.h 7 | ssmReferenceOptions.h 8 | ssmCorrespondenceOptions.h 9 | ssmModelBuildingOptions.h 10 | ) 11 | 12 | add_custom_target(Options ALL SOURCES ${HEADER}) 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | CMakeCache.txt 31 | CMakeFiles 32 | CMakeScripts 33 | Makefile 34 | cmake_install.cmake 35 | install_manifest.txt 36 | -------------------------------------------------------------------------------- /Modules/SSM/ssmTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const unsigned int Dimension = 3; 9 | 10 | typedef itk::Image BinaryImageType; 11 | typedef itk::Image FloatImageType; 12 | 13 | typedef itk::Point PointType; 14 | typedef itk::PointSet PointSetType; 15 | typedef itk::Mesh MeshType; 16 | 17 | typedef itk::StatisticalModel ShapeModelType; 18 | typedef itk::StandardMeshRepresenter RepresenterType; 19 | typedef std::vector MeshVectorType; 20 | 21 | typedef std::vector StringVector; 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Statistical Shape Modeling. 2 | Framework for statistical shape modeling with statismo. 3 | 4 | License 5 | 6 | Copyright (c) 2016 Aligned Research Group LLC. All right reserved. 7 | 8 | This code is free software; you can redistribute it and/or modify it under the terms 9 | of the GNU Lesser General Public License as published by the Free Software Foundation; 10 | either version 2.1 of the License, or (at your option) any later version. 11 | 12 | This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | See the GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License along with this library; 17 | if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -------------------------------------------------------------------------------- /Modules/SSM/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SSM) 2 | 3 | set(HEADER 4 | ssmTypes.h 5 | ssmImage3DMeshSource.h 6 | ssmImage3DMeshSource.hxx 7 | ssmInitializeSpatialTransform.h 8 | ssmShapeModelMultiTransform.h 9 | ssmShapeModelMultiTransform.hxx 10 | ssmMeshToLevelSetImageFilter.h 11 | ssmMeshToLevelSetImageFilter.hxx 12 | ssmMeshToImageRegistrationMethod.h 13 | ssmMeshToImageRegistrationMethod.hxx 14 | ssmShapeModelRegistrationMethodBase.h 15 | ssmShapeModelRegistrationMethodBase.hxx 16 | ssmShapeModelToImageRegistrationMethod.h 17 | ssmShapeModelToImageRegistrationMethod.hxx 18 | ssmShapeModelToLevelSetImageMetric.h 19 | ssmShapeModelToLevelSetImageMetric.hxx 20 | ssmPointSetToPointSetMetrics.h 21 | ssmPointSetToImageMetrics.h 22 | ssmMeshPropertiesCalculator.h 23 | ssmMeshPropertiesCalculator.hxx 24 | ssmBinaryImageToLevelSetImageFilter.h 25 | ) 26 | 27 | add_custom_target(SSM ALL SOURCES ${HEADER}) 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](http://178.62.239.31:8080/buildStatus/icon?job=SSM-nightly)](http://178.62.239.31:8080/job/SSM-nightly/) 2 | 3 | ##Statistical Shape Modeling 4 | 5 | Project for statistical shape modeling with [statismo](https://github.com/statismo/statismo). 6 | 7 | [Project Wiki](https://github.com/sMedX/StatisticalShapeModeling/wiki) 8 | 9 | Corresponding author Ruslan N. Kosarev 10 | 11 | E-mail address: kosarev@smedx.com 12 | 13 | 14 | ##License 15 | 16 | Copyright (c) 2016 Aligned Research Group LLC. All right reserved. 17 | 18 | This code is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. 19 | 20 | This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 21 | 22 | You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 | 24 | 25 | -------------------------------------------------------------------------------- /Applications/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SSM) 2 | 3 | add_executable(ssm-build-shapemodel ssm-build-shapemodel.cxx) 4 | target_link_libraries(ssm-build-shapemodel ${Boost_PROGRAM_OPTIONS_LIBRARY} ${statismo_LIBRARIES} ${ITK_LIBRARIES} ${VTK_LIBRARIES}) 5 | 6 | add_executable(ssm-extract-surface ssm-extract-surface.cxx) 7 | target_link_libraries(ssm-extract-surface ${Boost_PROGRAM_OPTIONS_LIBRARY} ${statismo_LIBRARIES} ${ITK_LIBRARIES} ${VTK_LIBRARIES}) 8 | 9 | add_executable(ssm-align-surfaces ssm-align-surfaces.cxx) 10 | target_link_libraries(ssm-align-surfaces ${Boost_PROGRAM_OPTIONS_LIBRARY} ${statismo_LIBRARIES} ${ITK_LIBRARIES}) 11 | 12 | add_executable(ssm-build-reference ssm-build-reference.cxx) 13 | target_link_libraries(ssm-build-reference ${Boost_PROGRAM_OPTIONS_LIBRARY} ${statismo_LIBRARIES} ${ITK_LIBRARIES}) 14 | 15 | add_executable(ssm-establish-correspondence ssm-establish-correspondence.cxx) 16 | target_link_libraries(ssm-establish-correspondence ${Boost_PROGRAM_OPTIONS_LIBRARY} ${statismo_LIBRARIES} ${ITK_LIBRARIES}) 17 | 18 | add_executable(ssm-cross-validation ssm-cross-validation.cxx) 19 | target_link_libraries(ssm-cross-validation ${Boost_PROGRAM_OPTIONS_LIBRARY} ${statismo_LIBRARIES} ${ITK_LIBRARIES}) 20 | 21 | add_executable(ssm-fit-shapemodel-to-surface ssm-fit-shapemodel-to-surface.cxx) 22 | target_link_libraries(ssm-fit-shapemodel-to-surface ${Boost_PROGRAM_OPTIONS_LIBRARY} ${statismo_LIBRARIES} ${ITK_LIBRARIES}) 23 | add_executable(ssm-specificity ssm-specificity.cxx) 24 | target_link_libraries(ssm-specificity ${Boost_PROGRAM_OPTIONS_LIBRARY} ${statismo_LIBRARIES} ${ITK_LIBRARIES}) 25 | 26 | add_executable(ssm-fit-shapemodel-to-image ssm-fit-shapemodel-to-image.cxx) 27 | target_link_libraries(ssm-fit-shapemodel-to-image ${Boost_PROGRAM_OPTIONS_LIBRARY} ${statismo_LIBRARIES} ${ITK_LIBRARIES}) 28 | -------------------------------------------------------------------------------- /Modules/Options/ssmModelSpecificityOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ssmOptionsBase.h" 4 | 5 | namespace ssm 6 | { 7 | //========================================================================= 8 | // Model specificity options 9 | //========================================================================= 10 | class ModelSpecificityOptions : public OptionsBase 11 | { 12 | public: 13 | 14 | ModelSpecificityOptions() 15 | { 16 | this->SetNameOfGroup("MODELQUALITY"); 17 | 18 | // initialize ptree 19 | this->Put("inplist", ""); 20 | this->Put("model", ""); 21 | this->Put("specificity.report", "", 0); 22 | this->Put("specificity.samples", 1000, 0); 23 | 24 | // initialize description 25 | po::options_description mandatoryOptions("Mandatory options"); 26 | mandatoryOptions.add_options() 27 | ("inplist,i", po::value(), "Input file with a list of files of input surfaces.") 28 | ("model,m", po::value(), "Input model file.") 29 | ; 30 | 31 | po::options_description inputOptions("Optional input options"); 32 | inputOptions.add_options() 33 | ("report,r", po::value(), "Output report file") 34 | ("samples", po::value()->default_value(this->GetDefaultValue("specificity.samples")), "The number of random samples to compute specificity.") 35 | ; 36 | 37 | this->AddToDescription(mandatoryOptions); 38 | this->AddToDescription(inputOptions); 39 | } 40 | 41 | bool ParseOptions(int argc, char** argv) 42 | { 43 | if (!OptionsBase::ParseOptions(argc, argv)) { 44 | return false; 45 | } 46 | 47 | return checkFileName(GetReportFileName()); 48 | } 49 | 50 | std::string GetInputList() const { return this->Get("inplist"); } 51 | std::string GetModelFileName() const { return this->Get("model"); } 52 | std::string GetReportFileName() const { return this->Get("specificity.report"); } 53 | size_t GetNumberOfSamples() const { return this->Get("specificity.samples"); } 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /Modules/Options/ssmModelCrossValidationOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ssmOptionsBase.h" 4 | 5 | namespace ssm 6 | { 7 | //========================================================================= 8 | // Cross validation options 9 | //========================================================================= 10 | class ModelCrossValidationOptions : public OptionsBase 11 | { 12 | public: 13 | 14 | ModelCrossValidationOptions() 15 | { 16 | SetNameOfGroup("MODELQUALITY"); 17 | 18 | // initialize ptree 19 | Put("inplist", ""); 20 | Put("cvtest.report", "", 0); 21 | Put("cvtest.components", -1, 0); 22 | Put("cvtest.write", false, 0); 23 | 24 | // initialize description 25 | po::options_description mandatoryOptions("Mandatory options"); 26 | mandatoryOptions.add_options() 27 | ("inplist,i", po::value(), "Input file with a list of files of input surfaces.") 28 | ; 29 | 30 | po::options_description inputOptions("Optional input options"); 31 | inputOptions.add_options() 32 | ("report,r", po::value(), "Output report file") 33 | ("components", po::value()->default_value(this->GetDefaultValue("cvtest.components")), "Number of components to reduce shape model for cross validation test.") 34 | ("write", po::value()->default_value(this->GetDefaultValue("cvtest.write")), "Write surfaces.") 35 | ; 36 | 37 | this->AddToDescription(mandatoryOptions); 38 | this->AddToDescription(inputOptions); 39 | } 40 | 41 | bool ParseOptions(int argc, char** argv) 42 | { 43 | if (!OptionsBase::ParseOptions(argc, argv)) { 44 | return false; 45 | } 46 | 47 | return checkFileName(GetReportFileName()); 48 | } 49 | 50 | std::string GetInputList() const { return this->Get("inplist"); } 51 | std::string GetReportFileName()const { return this->Get("cvtest.report"); } 52 | int GetNumberOfComponents() const { return this->Get("cvtest.components"); } 53 | bool GetWrite() const { return this->Get("cvtest.write"); } 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | 3 | project(StatisticalShapeModeling) 4 | 5 | set(CMAKE_CXX_STANDARD 11) # C++11... 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) #...is required... 7 | set(CMAKE_CXX_EXTENSIONS OFF) #...without compiler extensions like gnu++11 8 | 9 | # Find Boost 10 | set(Boost_LIBRARY_DIR "" CACHE PATH "") 11 | find_package(Boost 1.60 REQUIRED COMPONENTS filesystem program_options) 12 | include_directories(${Boost_INCLUDE_DIRS}) 13 | 14 | # Boost setup 15 | if(WIN32) 16 | # Disable autolinking in boost 17 | add_definitions(-DBOOST_ALL_NO_LIB) 18 | add_definitions(-DBOOST_ALL_DYN_LINK) 19 | endif() 20 | 21 | # Find Statismo 22 | set(statismo_SOURCE_DIR CACHE PATH "") 23 | find_package(statismo REQUIRED) 24 | 25 | if(NOT statismo_SOURCE_DIR) 26 | message(FATAL_ERROR "'statismo_SOURCE_DIR' is required but not specified") 27 | endif() 28 | 29 | list(APPEND statismo_INCLUDE_DIRS ${statismo_SOURCE_DIR}/modules/ITK/cli) 30 | include_directories(${statismo_INCLUDE_DIRS}) 31 | 32 | # Find ITK 33 | find_package(ITK REQUIRED) 34 | include(${ITK_USE_FILE}) 35 | 36 | if(${ITKVtkGlue_LOADED}) 37 | else() 38 | message(FATAL_ERROR "'ITKVtkGlue' module is required but it was not enabled in ITK '${ITK_DIR}'") 39 | endif() 40 | 41 | # Find VTK 42 | find_package(VTK REQUIRED) 43 | include(${VTK_USE_FILE}) 44 | 45 | find_package(OpenMP) 46 | if (OPENMP_FOUND) 47 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 48 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 49 | endif() 50 | 51 | # ------------------------------------------------------------------------------ 52 | # Modules and applications 53 | # ------------------------------------------------------------------------------ 54 | include_directories(${CMAKE_SOURCE_DIR}/Modules/SSM) 55 | include_directories(${CMAKE_SOURCE_DIR}/Modules/Utils) 56 | include_directories(${CMAKE_SOURCE_DIR}/Modules/Options) 57 | 58 | add_subdirectory(${CMAKE_SOURCE_DIR}/Modules/SSM) 59 | add_subdirectory(${CMAKE_SOURCE_DIR}/Modules/Options) 60 | add_subdirectory(${CMAKE_SOURCE_DIR}/Modules/Utils) 61 | add_subdirectory(${CMAKE_SOURCE_DIR}/Applications) 62 | -------------------------------------------------------------------------------- /Modules/Options/ssmModelBuildingOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ssmOptionsBase.h" 4 | 5 | namespace ssm 6 | { 7 | //========================================================================= 8 | // Surface extraction options 9 | //========================================================================= 10 | class ModelBuildingOptions : public OptionsBase 11 | { 12 | public: 13 | 14 | ModelBuildingOptions() 15 | { 16 | SetNameOfGroup("MODELBUILDING"); 17 | 18 | // initialize ptree 19 | Put("inplist", ""); 20 | Put("output", ""); 21 | 22 | Put("mode", "GPA", 0); 23 | Put("reference", "", 0); 24 | Put("noise", 0, 0); 25 | 26 | // initialize description 27 | po::options_description mandatoryOptions("Mandatory options"); 28 | mandatoryOptions.add_options() 29 | ("inplist,i", po::value(), "File containing a list of meshes to build shape model from") 30 | ("output,o", po::value(), "Name of the output file") 31 | ; 32 | 33 | po::options_description inputOptions("Optional input options"); 34 | inputOptions.add_options() 35 | ("mode", po::value()->default_value("GPA"), "Specify how the data is aligned: REFERENCE aligns all datasets rigidly to the reference and GPA aligns all datasets to the population mean.") 36 | ("reference", po::value(), "Specify the reference used for model building. This is needed if --alignment is REFERENCE") 37 | ("noise", po::value()->default_value(0), "Noise variance of the PPCA model") 38 | ; 39 | 40 | this->AddToDescription(mandatoryOptions); 41 | this->AddToDescription(inputOptions); 42 | } 43 | 44 | std::string GetInputList() const {return this->Get("inplist");} 45 | std::string GetOutputFileName() const {return this->Get("output");} 46 | std::string GetReferenceFileName() const {return this->Get("reference");} 47 | double GetNoise() const {return this->Get("noise");} 48 | std::string GetMode() const {return this->Get("mode");} 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /Modules/Options/ssmAlignmentOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ssmOptionsBase.h" 4 | 5 | namespace ssm 6 | { 7 | //========================================================================= 8 | // Alignment options 9 | //========================================================================= 10 | class AlignmentOptions : public OptionsBase 11 | { 12 | public: 13 | 14 | AlignmentOptions() 15 | { 16 | SetNameOfGroup("ALIGNMENT"); 17 | 18 | // initialize ptree 19 | Put("inplist", ""); 20 | Put("outlist", ""); 21 | Put("output", ""); 22 | Put("report", ""); 23 | Put("reference", ""); 24 | 25 | Put("transform", 2, false); 26 | Put("stages", 3, false); 27 | Put("iterations", 1000, false); 28 | } 29 | 30 | bool ParseOptions(int argc, char** argv) 31 | { 32 | if (!OptionsBase::ParseOptions(argc, argv)) { 33 | return false; 34 | } 35 | 36 | return checkFileName(GetReportFileName()); 37 | } 38 | 39 | 40 | std::string FormatOutput(const std::string & fileName) 41 | { 42 | const auto & format = Get("output"); 43 | try { 44 | return (boost::format(format) % getBaseNameFromPath(fileName)).str(); 45 | } 46 | catch (const boost::io::format_error &e) { 47 | std::cerr << "Could not format string with format " << format << std::endl; 48 | std::cout << e.what() << std::endl; 49 | throw; 50 | } 51 | } 52 | 53 | std::string GetInputList() const { return this->Get("inplist"); } 54 | std::string GetOutputList() const { return this->Get("outlist"); } 55 | std::string GetReportFileName() const { return this->Get("report"); } 56 | std::string GetReferenceFileName() const { return this->Get("reference"); } 57 | size_t GetNumberOfStages() const { return this->Get("stages"); } 58 | size_t GetNumberOfIterations() const { return this->Get("iterations"); } 59 | size_t GetTransform() const { return this->Get("transform"); } 60 | 61 | private: 62 | std::string m_InputFileName; 63 | std::string m_OutputFileName; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /Modules/SSM/ssmMeshToLevelSetImageFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ssm 10 | { 11 | template 12 | class MeshToLevelSetImageFilter : public itk::ImageSource 13 | { 14 | public: 15 | typedef MeshToLevelSetImageFilter Self; 16 | typedef itk::ImageSource Superclass; 17 | typedef itk::SmartPointer Pointer; 18 | typedef itk::SmartPointer ConstPointer; 19 | 20 | itkNewMacro(Self); 21 | itkTypeMacro(MeshToLevelSetImageFilter, itk::ImageSource); 22 | 23 | /** Constants for the image dimensions */ 24 | itkStaticConstMacro(Dimension, unsigned int, 3U); 25 | static_assert(TInputMesh::PointDimension == Dimension, "Invalid dimension of the input mesh."); 26 | static_assert(TOutputImage::ImageDimension == Dimension, "Invalid dimension of the output image."); 27 | 28 | typedef TInputMesh InputMeshType; 29 | typedef TOutputImage OutputImageType; 30 | typedef unsigned char BinaryPixelype; 31 | typedef itk::Image BinaryImageType; 32 | typedef typename OutputImageType::PointType ImagePointType; 33 | typedef typename OutputImageType::SpacingType SpacingType; 34 | typedef typename TOutputImage::SizeType SizeType; 35 | 36 | /** Set the mesh of this process object. */ 37 | using Superclass::SetInput; 38 | void SetInput(InputMeshType *input); 39 | 40 | /** Get the mesh input of this process object. */ 41 | InputMeshType * GetInput(); 42 | InputMeshType * GetInput(unsigned int idx); 43 | 44 | itkGetObjectMacro(Mask, BinaryImageType); 45 | 46 | // parameters 47 | itkSetMacro(Margin, double); 48 | itkGetMacro(Margin, double); 49 | 50 | void SetOrigin(const ImagePointType & point); 51 | itkGetMacro(Origin, ImagePointType); 52 | 53 | void SetSize(const SizeType & size); 54 | itkGetMacro(Size, SizeType); 55 | 56 | itkSetMacro(Spacing, SpacingType); 57 | itkGetMacro(Spacing, SpacingType); 58 | 59 | itkSetMacro(ForegroundValue, BinaryPixelype); 60 | itkGetMacro(ForegroundValue, BinaryPixelype); 61 | 62 | itkSetMacro(BackgroundValue, BinaryPixelype); 63 | itkGetMacro(BackgroundValue, BinaryPixelype); 64 | 65 | protected: 66 | MeshToLevelSetImageFilter(); 67 | virtual ~MeshToLevelSetImageFilter() {}; 68 | 69 | virtual void GenerateOutputInformation() ITK_OVERRIDE {} // do nothing 70 | virtual void GenerateData() ITK_OVERRIDE; 71 | 72 | typename BinaryImageType::Pointer m_Mask; 73 | ImagePointType m_Origin; 74 | bool m_UseOrigin; 75 | 76 | SpacingType m_Spacing; 77 | bool m_UseSpacing; 78 | 79 | SizeType m_Size; 80 | bool m_UseSize; 81 | 82 | double m_Margin; 83 | 84 | BinaryPixelype m_BackgroundValue; 85 | BinaryPixelype m_ForegroundValue; 86 | 87 | private: 88 | MeshToLevelSetImageFilter(const Self&); 89 | void operator=(const Self&); 90 | }; 91 | } 92 | 93 | #ifndef ITK_MANUAL_INSTANTIATION 94 | #include "ssmMeshToLevelSetImageFilter.hxx" 95 | #endif 96 | -------------------------------------------------------------------------------- /Modules/SSM/ssmShapeModelToImageRegistrationMethod.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ssmShapeModelRegistrationMethodBase.h" 9 | #include "ssmShapeModelToLevelSetImageMetric.h" 10 | 11 | namespace ssm 12 | { 13 | template 14 | class ShapeModelToImageRegistrationMethod : public ShapeModelRegistrationMethodBase 15 | { 16 | public: 17 | /** Standard class typedefs. */ 18 | typedef ShapeModelToImageRegistrationMethod Self; 19 | typedef ShapeModelRegistrationMethodBase Superclass; 20 | typedef itk::SmartPointer Pointer; 21 | typedef itk::SmartPointer ConstPointer; 22 | 23 | /** Method for creation through the object factory. */ 24 | itkNewMacro(Self); 25 | itkTypeMacro(ShapeModelToImageRegistrationMethod, Superclass); 26 | 27 | /** Constants for the image dimensions */ 28 | itkStaticConstMacro(ImageDimension, unsigned int, 3U); 29 | itkStaticConstMacro(PointDimension, unsigned int, itkGetStaticConstMacro(PointDimension)); 30 | static_assert(TInputImage::ImageDimension == ImageDimension, "Invalid dimension of input image. Dimension 3 is supported."); 31 | 32 | /** Typedefs of the shape model. */ 33 | typedef typename Superclass::ShapeModelType ShapeModelType; 34 | typedef typename Superclass::OutputMeshType OutputMeshType; 35 | typedef TInputImage InputImageType; 36 | 37 | /** Typedefs of the input image. */ 38 | typedef itk::Image BinaryImageType; 39 | typedef itk::Image LevelSetImageType; 40 | 41 | /** Type of the Transform Base class */ 42 | typedef typename Superclass::ShapeModelMultiTransformType ShapeModelMultiTransformType; 43 | typedef ShapeModelToLevelSetImageMetric MetricType; 44 | typedef itk::LBFGSOptimizer OptimizerType; 45 | 46 | // Set/Get level set image 47 | itkSetConstObjectMacro(Image, InputImageType); 48 | itkGetConstObjectMacro(Image, InputImageType); 49 | itkGetConstObjectMacro(LevelSetImage, LevelSetImageType); 50 | 51 | itkSetObjectMacro(Optimizer, OptimizerType); 52 | itkGetObjectMacro(Optimizer, OptimizerType); 53 | 54 | itkSetObjectMacro(Metric, MetricType); 55 | itkGetObjectMacro(Metric, MetricType); 56 | 57 | itkSetMacro(ComputeLevelSetImage, bool); 58 | itkGetMacro(ComputeLevelSetImage, bool); 59 | 60 | void PrintReport(); 61 | 62 | protected: 63 | ShapeModelToImageRegistrationMethod(); 64 | ~ShapeModelToImageRegistrationMethod() {}; 65 | 66 | void GenerateData(); 67 | void Initialize(); 68 | void ComputeLevelSet(); 69 | void InitializeMetric(); 70 | 71 | typename InputImageType::ConstPointer m_Image; 72 | typename LevelSetImageType::ConstPointer m_LevelSetImage; 73 | typename MetricType::Pointer m_Metric; 74 | bool m_ComputeLevelSetImage; 75 | }; 76 | } 77 | #ifndef ITK_MANUAL_INSTANTIATION 78 | #include "ssmShapeModelToImageRegistrationMethod.hxx" 79 | #endif 80 | -------------------------------------------------------------------------------- /Modules/Options/ssmExtractionOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ssmOptionsBase.h" 4 | 5 | namespace ssm 6 | { 7 | //========================================================================= 8 | // Surface extraction options 9 | //========================================================================= 10 | class ExtractionOptions : public OptionsBase 11 | { 12 | public: 13 | 14 | ExtractionOptions() 15 | { 16 | SetNameOfGroup("EXTRACTION"); 17 | 18 | // initialize ptree 19 | Put("inplist", ""); 20 | Put("outlist", ""); 21 | Put("output", ""); 22 | Put("report", ""); 23 | 24 | Put("sigma", 0, 0); 25 | Put("iterations", 100, 0); 26 | Put("points", 0, 0); 27 | 28 | // initialize description 29 | po::options_description mandatoryOptions("Mandatory options"); 30 | mandatoryOptions.add_options() 31 | ("input,i", po::value(), "Path to input image file.") 32 | ("output,o", po::value(), "Path for output surface file.") 33 | ; 34 | 35 | po::options_description inputOptions("Optional input options"); 36 | inputOptions.add_options() 37 | ("sigma", po::value()->default_value(this->GetDefaultValue("sigma")), "Sigma of the Gaussian kernel for RecursiveGaussianImageFilter to smooth input image") 38 | ("iterations", po::value()->default_value(this->GetDefaultValue("iterations")), "Number of iterations to adjust point positions for output surface") 39 | ("points", po::value()->default_value(this->GetDefaultValue("points")), "The number of points in output decimated surface (default value 0, i.e. no decimation)") 40 | ; 41 | 42 | po::options_description reportOptions("Optional report options"); 43 | reportOptions.add_options() 44 | ("report,r", po::value(), "Output report file") 45 | ; 46 | 47 | this->AddToDescription(mandatoryOptions); 48 | this->AddToDescription(inputOptions); 49 | this->AddToDescription(reportOptions); 50 | } 51 | 52 | bool ParseOptions(int argc, char** argv) 53 | { 54 | if (!OptionsBase::ParseOptions(argc, argv)) { 55 | return false; 56 | } 57 | 58 | if (ConfigIsEnabled()) { 59 | return checkFileName(GetReportFileName()); 60 | } 61 | 62 | return true; 63 | } 64 | 65 | std::string FormatOutput(const std::string & fileName) 66 | { 67 | const auto & format = this->Get("output"); 68 | try { 69 | return (boost::format(format) % getBaseNameFromPath(fileName)).str(); 70 | } 71 | catch (const boost::io::format_error &e) { 72 | std::cerr << "Could not format string with format " << format << std::endl; 73 | std::cout << e.what() << std::endl; 74 | throw; 75 | } 76 | } 77 | 78 | const std::string & GetInputFileName() const { return this->Get("input"); } 79 | const std::string & GetOutputFileName() const { return this->Get("output"); } 80 | std::string GetInputList() const { return this->Get("inplist"); } 81 | std::string GetOutputList() const { return this->Get("outlist"); } 82 | std::string GetReportFileName() const { return this->Get("report"); } 83 | double GetSigma() const { return this->Get("sigma"); } 84 | double GetFactor() const { return this->Get("factor"); } 85 | size_t GetNumberOfPoints() const { return this->Get("points"); } 86 | size_t GetNumberOfIterations() const { return this->Get("iterations"); } 87 | 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /Modules/SSM/ssmBinaryImageToLevelSetImageFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ssm 10 | { 11 | template< typename TInputImage, typename TOutputImage> 12 | class BinaryImageToLevelSetImageFilter : public itk::ImageToImageFilter 13 | { 14 | public: 15 | /** Standard class typedefs. */ 16 | typedef BinaryImageToLevelSetImageFilter Self; 17 | typedef itk::ImageToImageFilter< TInputImage, TOutputImage > Superclass; 18 | typedef itk::SmartPointer< Self > Pointer; 19 | 20 | /** Method for creation through the object factory. */ 21 | itkNewMacro(Self); 22 | 23 | /** Run-time type information (and related methods). */ 24 | itkTypeMacro(BinaryImageToLevelSetImageFilter, itk::ImageToImageFilter); 25 | 26 | protected: 27 | BinaryImageToLevelSetImageFilter() {}; 28 | virtual ~BinaryImageToLevelSetImageFilter() {} 29 | 30 | /** Does the real work. */ 31 | void GenerateData() 32 | { 33 | // define input image 34 | typename TInputImage::ConstPointer input = this->GetInput(); 35 | 36 | // compute minimum and maximum values 37 | typedef itk::MinimumMaximumImageCalculator MinimumMaximumImageCalculatorType; 38 | typename MinimumMaximumImageCalculatorType::Pointer labelValues = MinimumMaximumImageCalculatorType::New(); 39 | labelValues->SetImage(input); 40 | labelValues->Compute(); 41 | 42 | m_BackgroundValue = labelValues->GetMinimum(); 43 | m_ForegroundValue = labelValues->GetMaximum(); 44 | 45 | if (m_BackgroundValue == m_ForegroundValue) { 46 | itkExceptionMacro(<< "warning: there is no region of interest in the input image"); 47 | } 48 | 49 | // compute level set image 50 | typedef typename itk::SignedMaurerDistanceMapImageFilter DistanceFilterType; 51 | typename DistanceFilterType::Pointer distanceToForeground = DistanceFilterType::New(); 52 | distanceToForeground->SetInput(input); 53 | distanceToForeground->SetUseImageSpacing(true); 54 | distanceToForeground->SetBackgroundValue(m_BackgroundValue); 55 | distanceToForeground->SetInsideIsPositive(false); 56 | 57 | typename DistanceFilterType::Pointer distanceToBackground = DistanceFilterType::New(); 58 | distanceToBackground->SetInput(input); 59 | distanceToBackground->SetUseImageSpacing(true); 60 | distanceToBackground->SetBackgroundValue(m_ForegroundValue); 61 | distanceToBackground->SetInsideIsPositive(true); 62 | 63 | typedef typename itk::AddImageFilter AddImageFilterType; 64 | typename AddImageFilterType::Pointer add = AddImageFilterType::New(); 65 | add->SetInput1(distanceToForeground->GetOutput()); 66 | add->SetInput2(distanceToBackground->GetOutput()); 67 | 68 | typedef typename itk::MultiplyImageFilter MultiplyImageFilterType; 69 | typename MultiplyImageFilterType::Pointer multiply = MultiplyImageFilterType::New(); 70 | multiply->SetInput(add->GetOutput()); 71 | multiply->SetConstant(0.5); 72 | multiply->Update(); 73 | 74 | // set output 75 | this->GraftOutput(multiply->GetOutput()); 76 | } 77 | 78 | private: 79 | BinaryImageToLevelSetImageFilter(const Self &); //purposely not implemented 80 | void operator=(const Self &); //purposely not implemented 81 | bool m_UseImageSpacing = true; 82 | typename TInputImage::PixelType m_BackgroundValue; 83 | typename TInputImage::PixelType m_ForegroundValue; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /Modules/SSM/ssmMeshPropertiesCalculator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "itkAffineTransform.h" 4 | #include "itkImage.h" 5 | #include "itkSpatialObject.h" 6 | 7 | #include "vnl/vnl_vector_fixed.h" 8 | #include "vnl/vnl_matrix_fixed.h" 9 | #include "vnl/vnl_diag_matrix.h" 10 | 11 | namespace ssm 12 | { 13 | template< typename TMesh > 14 | class MeshPropertiesCalculator:public itk::Object 15 | { 16 | public: 17 | /** Standard class typedefs. */ 18 | typedef MeshPropertiesCalculator< TMesh > Self; 19 | typedef itk::Object Superclass; 20 | typedef itk::SmartPointer< Self > Pointer; 21 | typedef itk::SmartPointer< const Self > ConstPointer; 22 | 23 | /** Method for creation through the object factory. */ 24 | itkNewMacro(Self); 25 | 26 | /** Run-time type information (and related methods). */ 27 | itkTypeMacro(MeshPropertiesCalculator, itk::Object); 28 | 29 | /** Extract the dimension of the image. */ 30 | itkStaticConstMacro(Dimension, unsigned int, TMesh::PointDimension); 31 | 32 | /** Standard scalar type within this class. */ 33 | typedef double ScalarType; 34 | 35 | /** Standard vector type within this class. */ 36 | typedef itk::Vector< ScalarType, Dimension> VectorType; 37 | 38 | /** Spatial Object type within this class. */ 39 | typedef itk::SpatialObject SpatialObjectType; 40 | 41 | /** Spatial Object member types used within this class. */ 42 | typedef typename SpatialObjectType::Pointer SpatialObjectPointer; 43 | typedef typename SpatialObjectType::ConstPointer SpatialObjectConstPointer; 44 | 45 | /** Standard matrix type within this class. */ 46 | typedef itk::Matrix< ScalarType, Dimension, Dimension> MatrixType; 47 | 48 | /** Standard image type and pointer within this class. */ 49 | typedef TMesh MeshType; 50 | typedef typename MeshType::ConstPointer MeshConstPointer; 51 | typedef itk::Image BinaryImageType; 52 | 53 | /** Affine transform for mapping to and from principal axis */ 54 | typedef itk::AffineTransform< double, itkGetStaticConstMacro(Dimension) > AffineTransformType; 55 | typedef typename AffineTransformType::Pointer AffineTransformPointer; 56 | 57 | /** Set the input image. */ 58 | virtual void SetMesh(const MeshType *mesh) 59 | { 60 | if ( m_Mesh != mesh ) { 61 | m_Mesh = mesh; 62 | this->Modified(); 63 | m_Valid = false; 64 | } 65 | } 66 | 67 | /** Set the spatial object mask. */ 68 | virtual void SetSpatialObjectMask(const itk::SpatialObject< itkGetStaticConstMacro(Dimension) > *so) 69 | { 70 | if ( m_SpatialObjectMask != so ) 71 | { 72 | m_SpatialObjectMask = so; 73 | this->Modified(); 74 | m_Valid = false; 75 | } 76 | } 77 | 78 | void Compute(); 79 | 80 | /** Get center of mask gravity, in physical coordinates.*/ 81 | VectorType GetCenterOfMaskGravity() const; 82 | 83 | /** Get radius.*/ 84 | ScalarType GetRadius() const; 85 | 86 | protected: 87 | MeshPropertiesCalculator(); 88 | virtual ~MeshPropertiesCalculator() {}; 89 | virtual void PrintSelf(std::ostream & os, itk::Indent indent) const ITK_OVERRIDE; 90 | 91 | private: 92 | MeshPropertiesCalculator(const Self &) ITK_DELETE_FUNCTION; 93 | void operator=(const Self &) ITK_DELETE_FUNCTION; 94 | 95 | bool m_Valid; 96 | VectorType m_CenterOfMaskGravity; 97 | ScalarType m_Radius; 98 | 99 | MeshConstPointer m_Mesh; 100 | SpatialObjectConstPointer m_SpatialObjectMask; 101 | typename BinaryImageType::Pointer m_Mask; 102 | }; 103 | } 104 | 105 | #ifndef ITK_MANUAL_INSTANTIATION 106 | #include "ssmMeshPropertiesCalculator.hxx" 107 | #endif 108 | -------------------------------------------------------------------------------- /Modules/Options/ssmReferenceOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ssmOptionsBase.h" 4 | 5 | namespace ssm 6 | { 7 | //========================================================================= 8 | // Surface reference options 9 | //========================================================================= 10 | class ReferenceOptions : public OptionsBase 11 | { 12 | public: 13 | 14 | ReferenceOptions() 15 | { 16 | SetNameOfGroup("REFERENCE"); 17 | 18 | // initialize ptree 19 | Put("input", ""); 20 | Put("output", ""); 21 | Put("report", ""); 22 | 23 | Put("sigma", 0, 0); 24 | Put("level", 0, 0); 25 | 26 | Put("smoothing", 0, 0); 27 | Put("factor", 0.2, 0); 28 | Put("iterations", 100, 0); 29 | 30 | Put("decimation", 0, 0); 31 | Put("points", 0, 0); 32 | 33 | // initialize description 34 | po::options_description mandatoryOptions("Mandatory options"); 35 | mandatoryOptions.add_options() 36 | ("input,i", po::value(), "The path to the input image file.") 37 | ("output,o", po::value(), "The path for the output surface file.") 38 | ; 39 | 40 | po::options_description inputOptions("Optional input options"); 41 | inputOptions.add_options() 42 | ("sigma", po::value()->default_value(this->GetDefaultValue("sigma")), "The sigma of the Gaussian kernel measured in world coordinates.") 43 | ("level", po::value()->default_value(this->GetDefaultValue("level")), "The level value to extract surface from input level set image.") 44 | ("smoothing", po::value()->default_value(this->GetDefaultValue("smoothing")), "The method for surface smoothing \n 0 --- None \n 1 --- vtkWindowedSincPolyDataFilter \n 2 --- vtkSmoothPolyDataFilter.") 45 | ("factor", po::value()->default_value(this->GetDefaultValue("factor")), "The relaxation factor for Laplacian smoothing.") 46 | ("iterations", po::value()->default_value(this->GetDefaultValue("iterations")), "The number of iterations.") 47 | ("decimation", po::value()->default_value(this->GetDefaultValue("decimation")), "The method for surface decimation \n 0 --- None \n 1 --- vtkQuadricDecimation \n 2 --- vtkDecimatePro.") 48 | ("points", po::value()->default_value(this->GetDefaultValue("points")), "The number of points in output surface.") 49 | ; 50 | 51 | po::options_description reportOptions("Optional report options"); 52 | reportOptions.add_options() 53 | ("report,r", po::value(), "The path for the file to print report.") 54 | ; 55 | 56 | this->AddToDescription(mandatoryOptions); 57 | this->AddToDescription(inputOptions); 58 | this->AddToDescription(reportOptions); 59 | } 60 | 61 | bool ParseOptions(int argc, char** argv) 62 | { 63 | if (!OptionsBase::ParseOptions(argc, argv)) { 64 | return false; 65 | } 66 | 67 | return checkFileName(GetReportFileName()); 68 | } 69 | 70 | std::string GetInputFileName() const {return this->Get("input");} 71 | std::string GetOutputFileName() const {return this->Get("output");} 72 | std::string GetReportFileName() const {return this->Get("report");} 73 | double GetSigma() const {return this->Get("sigma");} 74 | double GetLevelValue() const {return this->Get("level");} 75 | size_t GetNumberOfPoints() const {return this->Get("points");} 76 | size_t GetDecimation() const {return this->Get("decimation");} 77 | size_t GetSmoothing() const {return this->Get("smoothing");} 78 | double GetRelaxationFactor() const {return this->Get("factor");} 79 | size_t GetNumberOfIterations() const {return this->Get("iterations");} 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /Modules/SSM/ssmShapeModelToImageRegistrationMethod.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ssmShapeModelToImageRegistrationMethod.h" 6 | #include "ssmBinaryImageToLevelSetImageFilter.h" 7 | 8 | namespace ssm 9 | { 10 | //---------------------------------------------------------------------------- 11 | template 12 | ShapeModelToImageRegistrationMethod::ShapeModelToImageRegistrationMethod() 13 | { 14 | m_Image = nullptr; 15 | m_Metric = MetricType::New(); 16 | 17 | m_LevelSetImage = nullptr; 18 | m_ComputeLevelSetImage = true; 19 | } 20 | //---------------------------------------------------------------------------- 21 | // 22 | template 23 | void ShapeModelToImageRegistrationMethod::Initialize() 24 | { 25 | if (!m_Image) { 26 | itkExceptionMacro(<< "Image is not initialized."); 27 | } 28 | 29 | Superclass::Initialize(); 30 | 31 | this->ComputeLevelSet(); 32 | this->InitializeTransform(); 33 | this->InitializeMetric(); 34 | this->InitializeOptimizer(); 35 | } 36 | //---------------------------------------------------------------------------- 37 | template 38 | void ShapeModelToImageRegistrationMethod::ComputeLevelSet() 39 | { 40 | // compute level set image 41 | if (m_ComputeLevelSetImage) { 42 | typedef ssm::BinaryImageToLevelSetImageFilter BinaryImageToLevelSetImageType; 43 | auto levelset = BinaryImageToLevelSetImageType::New(); 44 | levelset->SetInput(m_Image); 45 | levelset->Update(); 46 | m_LevelSetImage = levelset->GetOutput(); 47 | } 48 | else { 49 | typedef itk::CastImageFilter CastFilterType; 50 | auto cast = CastFilterType::New(); 51 | cast->SetInput(m_Image); 52 | cast->Update(); 53 | m_LevelSetImage = cast->GetOutput(); 54 | } 55 | } 56 | //---------------------------------------------------------------------------- 57 | // initialize metric 58 | template 59 | void ShapeModelToImageRegistrationMethod::InitializeMetric() 60 | { 61 | m_Metric->SetShapeModel(this->m_ShapeModel); 62 | m_Metric->SetLevelSetImage(this->m_LevelSetImage); 63 | m_Metric->SetTransform(this->m_Transform); 64 | m_Metric->SetLogger(this->m_Logger); 65 | try { 66 | m_Metric->Initialize(); 67 | } 68 | catch (itk::ExceptionObject& excep) { 69 | this->ExceptionHandler(excep, __LINE__); 70 | } 71 | Superclass::SetMetric(m_Metric); 72 | } 73 | //---------------------------------------------------------------------------- 74 | // 75 | template 76 | void ShapeModelToImageRegistrationMethod::GenerateData() 77 | { 78 | this->m_Time.Start(); 79 | 80 | // initialize the interconnects between components 81 | try { 82 | this->Initialize(); 83 | } 84 | catch (itk::ExceptionObject & excep) { 85 | throw excep; 86 | } 87 | 88 | // perform registration 89 | this->PerformRegistration(); 90 | 91 | // generate output data and print report 92 | this->GenerateOutputData(); 93 | } 94 | //---------------------------------------------------------------------------- 95 | // print report 96 | template 97 | void ShapeModelToImageRegistrationMethod::PrintReport() 98 | { 99 | m_Metric->PrintReport(); 100 | Superclass::PrintReport(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Modules/Options/ssmCorrespondenceOptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ssmOptionsBase.h" 4 | 5 | namespace ssm 6 | { 7 | //========================================================================= 8 | // Establish correspondence options 9 | //========================================================================= 10 | class CorrespondenceOptions : public OptionsBase 11 | { 12 | public: 13 | 14 | CorrespondenceOptions() 15 | { 16 | SetNameOfGroup("CORRESPONDENCE"); 17 | 18 | // initialize ptree 19 | Put("inplist", ""); 20 | Put("outlist", ""); 21 | Put("output", ""); 22 | Put("report", ""); 23 | Put("reference", ""); 24 | 25 | Put("transform", 3); 26 | Put("stages", 1); 27 | Put("iterations", 1000); 28 | 29 | Put("gpmodel.scale", 50); 30 | Put("gpmodel.parameters", 50); 31 | Put("gpmodel.components", 100); 32 | Put("gpmodel.regularization", 0.10); 33 | } 34 | 35 | std::string FormatOutput(const std::string & fileName) 36 | { 37 | const auto & format = Get("output"); 38 | try { 39 | return (boost::format(format) % getBaseNameFromPath(fileName)).str(); 40 | } 41 | catch (const boost::io::format_error &e) { 42 | std::cerr << "Could not format string with format " << format << std::endl; 43 | std::cout << e.what() << std::endl; 44 | throw; 45 | } 46 | } 47 | 48 | bool ParseOptions(int argc, char** argv) 49 | { 50 | if (!OptionsBase::ParseOptions(argc, argv)) { 51 | return false; 52 | } 53 | 54 | if (!CheckOptions()) { 55 | return false; 56 | } 57 | 58 | return true; 59 | } 60 | 61 | bool CheckOptions() 62 | { 63 | try { 64 | m_Parameters = this->GetAsVector("gpmodel.parameters"); 65 | m_Components = this->GetAsVector("gpmodel.components"); 66 | m_Regularization = this->GetAsVector("gpmodel.regularization"); 67 | } 68 | catch (...) { 69 | return false; 70 | } 71 | 72 | size_t numberOfStages = m_Parameters.size(); 73 | 74 | if (numberOfStages == 0 || m_Components.size() == 0 || m_Regularization.size() == 0) { 75 | std::cerr << "parameters, components and regularization factors must be specified" << std::endl; 76 | return false; 77 | } 78 | 79 | for (size_t n = m_Components.size(); n < numberOfStages; ++n) { 80 | m_Components.push_back(m_Components.back()); 81 | } 82 | 83 | for (size_t n = m_Regularization.size(); n < numberOfStages; ++n) { 84 | m_Regularization.push_back(m_Regularization.back()); 85 | } 86 | 87 | return checkFileName(GetReportFileName()); 88 | } 89 | 90 | std::string GetInputList() const { return this->Get("inplist"); } 91 | std::string GetOutputList() const { return this->Get("outlist"); } 92 | std::string GetReportFileName() const { return this->Get("report"); } 93 | std::string GetReferenceFileName() const { return this->Get("reference"); } 94 | size_t GetNumberOfStages() const { return this->Get("stages"); } 95 | size_t GetNumberOfIterations() const { return this->Get("iterations"); } 96 | size_t GetTransform() const { return this->Get("transform"); } 97 | double GetGPModelScale() const { return this->Get("gpmodel.scale"); } 98 | std::vector GetGPModelParameters() const { return m_Parameters; } 99 | std::vector GetGPModelNumberOfComponents() const { return m_Components; } 100 | std::vector GetGPModelRegularization() const { return m_Regularization; } 101 | 102 | private: 103 | std::vector m_Regularization; 104 | std::vector m_Parameters; 105 | std::vector m_Components; 106 | }; 107 | } 108 | -------------------------------------------------------------------------------- /Applications/ssm-specificity.cxx: -------------------------------------------------------------------------------- 1 | #include "ssmTypes.h" 2 | #include "ssmUtils.h" 3 | #include "ssmPointSetToPointSetMetrics.h" 4 | #include "ssmModelSpecificityOptions.h" 5 | 6 | double computeSpecificity(ShapeModelType::Pointer model, const MeshVectorType & vectorOfSurfaces, const size_t & numberOfSamples); 7 | 8 | int main(int argc, char** argv) 9 | { 10 | // parse options 11 | ssm::ModelSpecificityOptions options; 12 | 13 | if (!options.ParseOptions(argc, argv)) { 14 | return EXIT_FAILURE; 15 | } 16 | 17 | //---------------------------------------------------------------------------- 18 | // read statistical shape model 19 | typedef itk::StatisticalModel StatisticalModelType; 20 | auto model = StatisticalModelType::New(); 21 | try { 22 | model->Load(RepresenterType::New(), options.GetModelFileName().c_str()); 23 | } 24 | catch (itk::ExceptionObject & excep) { 25 | std::cerr << excep << std::endl; 26 | return EXIT_FAILURE; 27 | } 28 | 29 | // read list of files 30 | StringVector listOfFiles; 31 | try { 32 | listOfFiles = readListFromFile(options.GetInputList()); 33 | } 34 | catch (std::ifstream::failure & e) { 35 | std::cerr << e.what() << std::endl; 36 | return EXIT_FAILURE; 37 | } 38 | 39 | MeshVectorType vectorOfShapes; 40 | for (const auto & fileName : listOfFiles) { 41 | auto surface = MeshType::New(); 42 | if (!readMesh(surface, fileName)) { 43 | return EXIT_FAILURE; 44 | } 45 | vectorOfShapes.push_back(surface); 46 | } 47 | 48 | std::cout << "model " << options.GetModelFileName() << std::endl; 49 | std::cout << "number of random shapes " << options.GetNumberOfSamples() << std::endl; 50 | std::cout << "number of train shapes " << vectorOfShapes.size() << std::endl; 51 | std::cout << std::endl; 52 | 53 | // compute specificity 54 | double specificity = computeSpecificity(model, vectorOfShapes, options.GetNumberOfSamples()); 55 | 56 | std::cout << "specificity of the model " << specificity << std::endl; 57 | std::cout << std::endl; 58 | 59 | // print report to *.csv file 60 | if (options.GetReportFileName().size() > 0) { 61 | std::cout << "print report to the file " << options.GetReportFileName() << std::endl; 62 | std::ofstream file(options.GetReportFileName(), std::ofstream::out); 63 | file << "model;" << options.GetModelFileName() << std::endl; 64 | file << "number of train shapes;" << vectorOfShapes.size() << std::endl; 65 | file << "number of random shapes;" << options.GetNumberOfSamples() << std::endl; 66 | file << "specificity;" << specificity << std::endl; 67 | file.close(); 68 | } 69 | } 70 | 71 | double computeSpecificity(ShapeModelType::Pointer model, const MeshVectorType & vectorOfShapes, const size_t & numberOfSamples) 72 | { 73 | double specificity = 0; 74 | 75 | for (size_t count = 0; count < numberOfSamples; ++count) { 76 | 77 | double value = std::numeric_limits::max(); 78 | 79 | // generate random point set 80 | auto randomPoints = PointSetType::New(); 81 | randomPoints->SetPoints(model->DrawSample()->GetPoints()); 82 | 83 | for (const auto & shape : vectorOfShapes) { 84 | 85 | // get point set from train dataset 86 | auto trainPoints = PointSetType::New(); 87 | trainPoints->SetPoints(shape->GetPoints()); 88 | 89 | // compute metrics 90 | typedef ssm::PointSetToPointSetMetrics PointSetToPointSetMetricsType; 91 | auto metrics = PointSetToPointSetMetricsType::New(); 92 | metrics->SetFixedPointSet(randomPoints); 93 | metrics->SetMovingPointSet(trainPoints); 94 | metrics->Compute(); 95 | 96 | value = std::min(value, metrics->GetRMSEValue()); 97 | } 98 | 99 | specificity += value; 100 | 101 | std::cout << count + 1 << "/" << numberOfSamples << " distance: " << value << std::endl; 102 | } 103 | 104 | return specificity / numberOfSamples; 105 | } 106 | -------------------------------------------------------------------------------- /Modules/SSM/ssmMeshToImageRegistrationMethod.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ssmInitializeSpatialTransform.h" 10 | 11 | namespace ssm 12 | { 13 | template 14 | class MeshToImageRegistrationMethod : public itk::MeshToMeshFilter < TInputMesh, TOutputMesh > 15 | { 16 | public: 17 | // Standard typedefs 18 | typedef MeshToImageRegistrationMethod Self; 19 | typedef typename itk::MeshToMeshFilter Superclass; 20 | typedef typename itk::SmartPointer Pointer; 21 | typedef typename itk::SmartPointer ConstPointer; 22 | typedef itk::LBFGSOptimizer OptimizerType; 23 | typedef typename itk::Optimizer::ScalesType ScalesType; 24 | typedef typename itk::Transform TransformType; 25 | typedef typename itk::PointSet PointSetType; 26 | typedef typename itk::Image BinaryImageType; 27 | typedef typename itk::Image LevelsetImageType; 28 | typedef typename itk::PointSetToImageMetric MetricType; 29 | typedef typename itk::LinearInterpolateImageFunction InterpolatorType; 30 | typedef InitializeSpatialTransform InitializeTransformType; 31 | 32 | itkNewMacro(Self); 33 | itkTypeMacro(MeshToImageRegistrationMethod, itk::MeshToMeshFilter); 34 | 35 | // set type of transform 36 | itkSetEnumMacro(TypeOfTransform, InitializeTransformType::Transform); 37 | itkGetEnumMacro(TypeOfTransform, InitializeTransformType::Transform); 38 | void SetTypeOfTransform(size_t transform) { m_TypeOfTransform = static_cast(transform); } 39 | 40 | //Set/Get PotentialImage 41 | itkGetConstObjectMacro(LevelsetImage, LevelsetImageType); 42 | itkSetConstObjectMacro(LevelsetImage, LevelsetImageType); 43 | 44 | itkGetConstObjectMacro(Optimizer, OptimizerType); 45 | itkGetConstObjectMacro(Transform, TransformType); 46 | 47 | itkSetMacro(NumberOfIterations, unsigned int); 48 | itkGetMacro(NumberOfIterations, unsigned int); 49 | 50 | itkSetMacro(GradientConvergenceTolerance, double); 51 | itkGetMacro(GradientConvergenceTolerance, double); 52 | 53 | itkSetMacro(LineSearchAccuracy, double); 54 | itkGetMacro(LineSearchAccuracy, double); 55 | 56 | itkSetMacro(DefaultStepLength, double); 57 | itkGetMacro(DefaultStepLength, double); 58 | 59 | void PrintReport(std::ostream& os) const; 60 | 61 | protected: 62 | MeshToImageRegistrationMethod(); 63 | ~MeshToImageRegistrationMethod() {} 64 | 65 | virtual void GenerateData() override; 66 | void InitializeTransform(); 67 | void ComputeLabelImage(); 68 | void GenerateOutputData(); 69 | 70 | itkStaticConstMacro(PointDimension, unsigned int, TInputMesh::PointDimension); 71 | static_assert(PointDimension == 3U, "Invalid dimension of input mesh. Dimension 3 is supported."); 72 | 73 | OptimizerType::Pointer m_Optimizer; 74 | typename TInputMesh::Pointer m_Surface; 75 | typename PointSetType::Pointer m_PointSet; 76 | typename BinaryImageType::ConstPointer m_Mask; 77 | typename LevelsetImageType::ConstPointer m_LevelsetImage; 78 | typename MetricType::Pointer m_Metric; 79 | typename TransformType::Pointer m_Transform; 80 | typename InterpolatorType::Pointer m_Interpolator; 81 | 82 | InitializeTransformType::Transform m_TypeOfTransform = InitializeTransformType::Transform::Euler3D; 83 | size_t m_NumberOfIterations = 500; 84 | double m_LineSearchAccuracy = 0.1; 85 | double m_DefaultStepLength = 0.1; 86 | double m_GradientConvergenceTolerance = 1e-07; 87 | ScalesType m_Scales; 88 | }; 89 | } 90 | 91 | #ifndef ITK_MANUAL_INSTANTIATION 92 | #include "ssmMeshToImageRegistrationMethod.hxx" 93 | #endif 94 | -------------------------------------------------------------------------------- /Modules/SSM/ssmImage3DMeshSource.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace ssm 7 | { 8 | template< typename TInputImage, typename TOutputMesh > 9 | class Image3DMeshSource:public itk::ProcessObject 10 | { 11 | public: 12 | /** Standard "Self" typedef. */ 13 | typedef Image3DMeshSource Self; 14 | typedef itk::ProcessObject Superclass; 15 | typedef itk::SmartPointer< Self > Pointer; 16 | typedef itk::SmartPointer< const Self > ConstPointer; 17 | 18 | /** Method for creation through the object factory. */ 19 | itkNewMacro(Self); 20 | 21 | /** Run-time type information (and related methods). */ 22 | itkTypeMacro(Image3DMeshSource, itk::ProcessObject); 23 | 24 | /** Hold on to the type information specified by the template parameters. */ 25 | typedef TOutputMesh OutputMeshType; 26 | typedef vtkSmartPointer OutputMeshPointer; 27 | 28 | /** Input Image Type Definition. */ 29 | typedef TInputImage InputImageType; 30 | typedef typename InputImageType::Pointer InputImagePointer; 31 | typedef typename InputImageType::ConstPointer InputImageConstPointer; 32 | typedef typename InputImageType::PixelType InputPixelType; 33 | typedef typename InputImageType::SpacingType SpacingType; 34 | typedef typename InputImageType::PointType OriginType; 35 | typedef typename InputImageType::RegionType RegionType; 36 | typedef typename InputImageType::SizeType SizeType; 37 | 38 | /** Set/Get input and output data */ 39 | void SetInput(const TInputImage *image); 40 | OutputMeshType * GetOutput(); 41 | 42 | /** Decimation */ 43 | enum class Decimation 44 | { 45 | None, 46 | QuadricDecimation, 47 | DecimatePro 48 | }; 49 | 50 | itkSetEnumMacro(Decimation, Decimation); 51 | itkGetEnumMacro(Decimation, Decimation); 52 | void SetDecimation(const int & decimation) { this->SetDecimation(static_cast(decimation)); } 53 | 54 | /** Smoothing */ 55 | enum class Smoothing 56 | { 57 | None, 58 | WindowedSinc, 59 | Laplacian 60 | }; 61 | 62 | itkSetEnumMacro(Smoothing, Smoothing); 63 | itkGetEnumMacro(Smoothing, Smoothing); 64 | void SetSmoothing(const int & smoothing) { this->SetSmoothing(static_cast(smoothing)); } 65 | 66 | itkSetMacro(LevelValue, double); 67 | itkGetMacro(LevelValue, double); 68 | 69 | itkSetMacro(ComputeLevelValue, bool); 70 | itkGetMacro(ComputeLevelValue, bool); 71 | 72 | itkSetMacro(Sigma, double); 73 | itkGetMacro(Sigma, double); 74 | 75 | itkSetMacro(NumberOfIterations, size_t); 76 | itkGetMacro(NumberOfIterations, size_t); 77 | 78 | itkSetMacro(NumberOfPoints, size_t); 79 | itkGetMacro(NumberOfPoints, size_t); 80 | 81 | itkSetMacro(RelaxationFactor, double); 82 | itkGetMacro(RelaxationFactor, double); 83 | 84 | void Update() { this->GenerateData(); }; 85 | void PrintReport() const; 86 | 87 | protected: 88 | Image3DMeshSource(); 89 | ~Image3DMeshSource() {}; 90 | 91 | void GenerateData() ITK_OVERRIDE; 92 | void SurfaceDecimation(); 93 | void SurfaceSmoothing(); 94 | typename TInputImage::ConstPointer GetInput(); 95 | 96 | private: 97 | Image3DMeshSource(const Self &) ITK_DELETE_FUNCTION; 98 | void operator=(const Self &) ITK_DELETE_FUNCTION; 99 | 100 | typedef typename InputImageType::SizeType InputImageSizeType; 101 | 102 | void CreateMesh(); 103 | 104 | vtkSmartPointer m_Output; 105 | double m_LevelValue; 106 | bool m_ComputeLevelValue; 107 | 108 | /** temporary variables used in CreateMesh to avoid thousands of 109 | * calls to GetInput() and GetOutput() 110 | */ 111 | OutputMeshPointer m_OutputMesh; 112 | 113 | double m_Sigma; 114 | 115 | Smoothing m_Smoothing; 116 | size_t m_NumberOfIterations; 117 | double m_RelaxationFactor; 118 | double m_FeatureAngle; 119 | double m_PassBand; 120 | 121 | Decimation m_Decimation; 122 | double m_Reduction; 123 | size_t m_NumberOfPoints; 124 | }; 125 | } 126 | 127 | #ifndef ITK_MANUAL_INSTANTIATION 128 | #include "ssmImage3DMeshSource.hxx" 129 | #endif 130 | -------------------------------------------------------------------------------- /Applications/ssm-build-shapemodel.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "utils/statismo-build-models-utils.h" 14 | 15 | #include "ssmTypes.h" 16 | #include "ssmUtils.h" 17 | #include "ssmModelBuildingOptions.h" 18 | 19 | ShapeModelType::Pointer shapeModelBuilder(const StringVector & list, const ssm::ModelBuildingOptions & options); 20 | 21 | int main(int argc, char** argv) 22 | { 23 | // parse options 24 | ssm::ModelBuildingOptions options; 25 | 26 | if (!options.ParseOptions(argc, argv)) { 27 | return EXIT_FAILURE; 28 | } 29 | 30 | //---------------------------------------------------------------------------- 31 | // read list of files 32 | StringVector list; 33 | try { 34 | list = readListFromFile(options.GetInputList()); 35 | } 36 | catch (std::ifstream::failure & e) { 37 | std::cerr << e.what() << std::endl; 38 | return EXIT_FAILURE; 39 | } 40 | 41 | //---------------------------------------------------------------------------- 42 | // build shape model 43 | try { 44 | auto model = shapeModelBuilder(list, options); 45 | 46 | std::cout << "shape model saved to the file " << options.GetOutputFileName() << std::endl; 47 | std::cout << "number of components " << model->GetNumberOfPrincipalComponents() << std::endl; 48 | std::cout << "number of cells " << model->GetRepresenter()->GetReference()->GetNumberOfCells() << std::endl; 49 | std::cout << "number of points " << model->GetRepresenter()->GetReference()->GetNumberOfPoints() << std::endl; 50 | model->Save(options.GetOutputFileName().c_str()); 51 | } 52 | catch (itk::ExceptionObject & e) { 53 | std::cerr << "Could not build or save the shape model." << std::endl; 54 | std::cerr << e.what() << std::endl; 55 | return EXIT_FAILURE; 56 | } 57 | 58 | return EXIT_SUCCESS; 59 | } 60 | 61 | ShapeModelType::Pointer shapeModelBuilder(const StringVector & list, const ssm::ModelBuildingOptions & options) 62 | { 63 | auto representer = RepresenterType::New(); 64 | 65 | typedef itk::DataManager DataManagerType; 66 | auto dataManager = DataManagerType::New(); 67 | 68 | typedef itk::MeshFileReader MeshReaderType; 69 | typedef std::vector MeshReaderVector; 70 | MeshReaderVector meshes; 71 | 72 | for (const auto & fileName : list) { 73 | auto reader = MeshReaderType::New(); 74 | reader->SetFileName(fileName); 75 | reader->Update(); 76 | meshes.push_back(reader); 77 | } 78 | 79 | if (meshes.size() == 0) { 80 | itkGenericExceptionMacro(<< "The specified list of surfaces is empty."); 81 | } 82 | 83 | if (options.GetMode() == "reference") { 84 | typedef itk::MeshFileReader MeshReaderType; 85 | auto reader = MeshReaderType::New(); 86 | reader->SetFileName(options.GetReferenceFileName()); 87 | reader->Update(); 88 | representer->SetReference(reader->GetOutput()); 89 | } 90 | else { 91 | std::vector originalMeshes; 92 | for (const auto &reader : meshes) { 93 | originalMeshes.push_back(reader->GetOutput()); 94 | } 95 | 96 | const size_t numberOfIterations = 100; 97 | const size_t numberOfPoints = 1000; 98 | const double breakIfChangeBelow = 0.001; 99 | 100 | typedef itk::VersorRigid3DTransform< float > Rigid3DTransformType; 101 | typedef itk::LandmarkBasedTransformInitializer LandmarkBasedTransformInitializerType; 102 | typedef itk::TransformMeshFilter< MeshType, MeshType, Rigid3DTransformType > TransformFilterType; 103 | auto reference = calculateProcrustesMeanMesh(originalMeshes, numberOfIterations, numberOfPoints, breakIfChangeBelow); 104 | representer->SetReference(reference); 105 | } 106 | 107 | dataManager->SetRepresenter(representer); 108 | 109 | for (const auto &reader : meshes) { 110 | dataManager->AddDataset(reader->GetOutput(), reader->GetFileName()); 111 | } 112 | 113 | typedef itk::PCAModelBuilder ModelBuilderType; 114 | auto modelBuilder = ModelBuilderType::New(); 115 | auto model = modelBuilder->BuildNewModel(dataManager->GetData(), options.GetNoise()); 116 | 117 | return model; 118 | } 119 | -------------------------------------------------------------------------------- /Modules/SSM/ssmMeshToLevelSetImageFilter.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ssmMeshToLevelSetImageFilter.h" 10 | 11 | namespace ssm 12 | { 13 | template 14 | MeshToLevelSetImageFilter::MeshToLevelSetImageFilter() 15 | { 16 | this->SetNumberOfRequiredInputs(1); 17 | 18 | m_UseOrigin = false; 19 | m_UseSize = false; 20 | 21 | m_Margin = 0.10; 22 | m_Spacing.Fill(1); 23 | 24 | m_ForegroundValue = itk::NumericTraits< BinaryPixelype >::OneValue(); 25 | m_BackgroundValue = itk::NumericTraits< BinaryPixelype >::ZeroValue(); 26 | } 27 | 28 | /** Set the Input Mesh */ 29 | template< typename TInputMesh, typename TOutputImage > 30 | void MeshToLevelSetImageFilter< TInputMesh, TOutputImage >::SetInput(TInputMesh *input) 31 | { 32 | this->itk::ProcessObject::SetNthInput(0, input); 33 | } 34 | 35 | /** Get the input Mesh */ 36 | template< typename TInputMesh, typename TOutputImage > 37 | typename MeshToLevelSetImageFilter< TInputMesh, TOutputImage >::InputMeshType * 38 | MeshToLevelSetImageFilter< TInputMesh, TOutputImage >::GetInput(void) 39 | { 40 | return static_cast (this->itk::ProcessObject::GetInput(0)); 41 | } 42 | 43 | /** Get the input Mesh */ 44 | template< typename TInputMesh, typename TOutputImage > 45 | typename MeshToLevelSetImageFilter< TInputMesh, TOutputImage >::InputMeshType * 46 | MeshToLevelSetImageFilter< TInputMesh, TOutputImage >::GetInput(unsigned int idx) 47 | { 48 | return static_cast< TInputMesh * >(this->itk::ProcessObject::GetInput(idx)); 49 | } 50 | 51 | template 52 | void MeshToLevelSetImageFilter::SetOrigin(const ImagePointType & point) 53 | { 54 | m_Origin = point; 55 | m_UseOrigin = true; 56 | } 57 | 58 | template 59 | void MeshToLevelSetImageFilter::SetSize(const SizeType & size) 60 | { 61 | m_Size = size; 62 | m_UseSize = true; 63 | } 64 | 65 | template 66 | void MeshToLevelSetImageFilter::GenerateData() 67 | { 68 | auto bbox = this->GetInput()->GetBoundingBox(); 69 | auto diff = bbox->GetMaximum() - bbox->GetMinimum(); 70 | 71 | // compute origin and size 72 | for (size_t i = 0; i < Dimension; ++i) { 73 | if (!m_UseOrigin) { 74 | m_Origin[i] = bbox->GetMinimum()[i] - m_Margin * diff[i]; 75 | } 76 | 77 | if (!m_UseSize) { 78 | m_Size[i] = diff[i] * (1 + 2 * m_Margin) / m_Spacing[i]; 79 | } 80 | } 81 | 82 | typedef itk::TriangleMeshToBinaryImageFilter TriangleMeshToBinaryImageFilterType; 83 | auto surfaceToImage = TriangleMeshToBinaryImageFilterType::New(); 84 | surfaceToImage->SetInput(this->GetInput()); 85 | surfaceToImage->SetInsideValue(m_ForegroundValue); 86 | surfaceToImage->SetOutsideValue(m_BackgroundValue); 87 | surfaceToImage->SetSize(m_Size); 88 | surfaceToImage->SetSpacing(m_Spacing); 89 | surfaceToImage->SetOrigin(m_Origin); 90 | surfaceToImage->Update(); 91 | m_Mask = surfaceToImage->GetOutput(); 92 | 93 | // compute level set image 94 | typedef itk::SignedMaurerDistanceMapImageFilter DistanceFilterType; 95 | auto distanceToForeground = DistanceFilterType::New(); 96 | distanceToForeground->SetInput(surfaceToImage->GetOutput()); 97 | distanceToForeground->SetUseImageSpacing(true); 98 | distanceToForeground->SetBackgroundValue(m_BackgroundValue); 99 | distanceToForeground->SetInsideIsPositive(false); 100 | 101 | typename DistanceFilterType::Pointer distanceToBackground = DistanceFilterType::New(); 102 | distanceToBackground->SetInput(surfaceToImage->GetOutput()); 103 | distanceToBackground->SetUseImageSpacing(true); 104 | distanceToBackground->SetBackgroundValue(m_ForegroundValue); 105 | distanceToBackground->SetInsideIsPositive(true); 106 | 107 | typedef itk::AddImageFilter AddImageFilterType; 108 | auto add = AddImageFilterType::New(); 109 | add->SetInput1(distanceToForeground->GetOutput()); 110 | add->SetInput2(distanceToBackground->GetOutput()); 111 | 112 | typedef itk::MultiplyImageFilter FilterType; 113 | auto multiply = FilterType::New(); 114 | multiply->SetInput(add->GetOutput()); 115 | multiply->SetConstant(0.5); 116 | multiply->Update(); 117 | 118 | auto output = this->GetOutput(); 119 | output->Graft(multiply->GetOutput()); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Applications/ssm-build-reference.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ssmTypes.h" 7 | #include "ssmUtils.h" 8 | #include "ssmPointSetToImageMetrics.h" 9 | #include "ssmImage3DMeshSource.h" 10 | #include "ssmReferenceOptions.h" 11 | 12 | double averageLengthOfEdges(vtkPolyData* poly); 13 | double averageAreaOfCells(vtkPolyData* poly); 14 | 15 | int main(int argc, char** argv) 16 | { 17 | // parse options 18 | ssm::ReferenceOptions options; 19 | 20 | if (!options.ParseOptions(argc, argv)) { 21 | return EXIT_FAILURE; 22 | } 23 | 24 | //---------------------------------------------------------------------------- 25 | // read image 26 | auto image = FloatImageType::New(); 27 | if (!readImage(image, options.GetInputFileName())) { 28 | return false; 29 | } 30 | printImageInfo(image, options.GetInputFileName()); 31 | 32 | typedef itk::MultiplyImageFilter FilterType; 33 | auto multiply = FilterType::New(); 34 | multiply->SetInput(image); 35 | multiply->SetConstant(-1); 36 | multiply->Update(); 37 | image = multiply->GetOutput(); 38 | 39 | typedef ssm::Image3DMeshSource Image3DMeshSourceType; 40 | auto imageToSurface = Image3DMeshSourceType::New(); 41 | imageToSurface->SetInput(image); 42 | imageToSurface->SetSigma(options.GetSigma()); 43 | imageToSurface->SetLevelValue((-1) * options.GetLevelValue()); 44 | imageToSurface->SetComputeLevelValue(false); 45 | imageToSurface->SetSmoothing(options.GetSmoothing()); 46 | imageToSurface->SetRelaxationFactor(options.GetRelaxationFactor()); 47 | imageToSurface->SetNumberOfIterations(options.GetNumberOfIterations()); 48 | imageToSurface->SetDecimation(options.GetDecimation()); 49 | imageToSurface->SetNumberOfPoints(options.GetNumberOfPoints()); 50 | try { 51 | imageToSurface->Update(); 52 | } 53 | catch (itk::ExceptionObject& excep) { 54 | std::cerr << excep << std::endl; 55 | return false; 56 | } 57 | auto surface = imageToSurface->GetOutput(); 58 | 59 | // write polydata to the file 60 | if (!writeVTKPolydata(surface, options.GetOutputFileName())) { 61 | return false; 62 | } 63 | 64 | //---------------------------------------------------------------------------- 65 | // compute report 66 | typedef std::pair PairType; 67 | std::vector surfaceInfo; 68 | surfaceInfo.push_back(PairType("number of points", std::to_string(surface->GetNumberOfPoints()))); 69 | surfaceInfo.push_back(PairType(" number of cells", std::to_string(surface->GetNumberOfCells()))); 70 | surfaceInfo.push_back(PairType(" length of edges", std::to_string(averageLengthOfEdges(surface)))); 71 | surfaceInfo.push_back(PairType(" area of cells", std::to_string(averageAreaOfCells(surface)))); 72 | 73 | //---------------------------------------------------------------------------- 74 | // compute metrics 75 | typedef itk::PointSet PointSetType; 76 | typedef ssm::PointSetToImageMetrics PointSetToImageMetricsType; 77 | auto metrics = PointSetToImageMetricsType::New(); 78 | metrics->SetPointSetAsPolyData(surface); 79 | metrics->SetImage(image); 80 | metrics->SetInfo(surfaceInfo); 81 | try { 82 | metrics->Compute(); 83 | } 84 | catch (itk::ExceptionObject& excep) { 85 | std::cerr << excep << std::endl; 86 | return EXIT_FAILURE; 87 | } 88 | 89 | metrics->PrintReport(); 90 | metrics->PrintReportToFile(options.GetReportFileName(), getBaseNameFromPath(options.GetOutputFileName())); 91 | 92 | return EXIT_SUCCESS; 93 | } 94 | 95 | double averageLengthOfEdges(vtkPolyData*poly) 96 | { 97 | const unsigned int numberOfCells = poly->GetNumberOfCells(); 98 | double sum = 0; 99 | 100 | for (int n = 0; n < poly->GetNumberOfCells(); ++n) { 101 | double p1[3], p2[3], p3[3]; 102 | 103 | vtkSmartPointer cell = poly->GetCell(n); 104 | vtkSmartPointer points = cell->GetPoints(); 105 | 106 | points->GetPoint(0, p1); 107 | points->GetPoint(1, p2); 108 | points->GetPoint(2, p3); 109 | 110 | sum += sqrt(vtkMath::Distance2BetweenPoints(p1, p2)) + 111 | sqrt(vtkMath::Distance2BetweenPoints(p1, p3)) + 112 | sqrt(vtkMath::Distance2BetweenPoints(p2, p3)); 113 | } 114 | 115 | return sum / (3 * numberOfCells); 116 | } 117 | 118 | double averageAreaOfCells(vtkPolyData*poly) 119 | { 120 | const size_t numberOfCells = poly->GetNumberOfCells(); 121 | double sum = 0; 122 | 123 | for (size_t n = 0; n < numberOfCells; ++n) { 124 | double a[3], b[3], c[3]; 125 | double p1[3], p2[3], p3[3]; 126 | 127 | vtkSmartPointer cell = poly->GetCell(n); 128 | vtkSmartPointer points = cell->GetPoints(); 129 | 130 | points->GetPoint(0, p1); 131 | points->GetPoint(1, p2); 132 | points->GetPoint(2, p3); 133 | 134 | vtkMath::Subtract(p1, p2, a); 135 | vtkMath::Subtract(p1, p3, b); 136 | vtkMath::Cross(a, b, c); 137 | 138 | sum += sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]) / 2; 139 | } 140 | 141 | return sum / numberOfCells; 142 | } 143 | -------------------------------------------------------------------------------- /Modules/SSM/ssmShapeModelRegistrationMethodBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "ssmShapeModelMultiTransform.h" 12 | #include "ssmShapeModelToLevelSetImageMetric.h" 13 | 14 | namespace ssm 15 | { 16 | template 17 | class ShapeModelRegistrationMethodBase : public itk::ProcessObject 18 | { 19 | public: 20 | /** Standard class typedefs. */ 21 | typedef ShapeModelRegistrationMethodBase Self; 22 | typedef itk::ProcessObject Superclass; 23 | typedef itk::SmartPointer< Self > Pointer; 24 | typedef itk::SmartPointer< const Self > ConstPointer; 25 | 26 | /** Method for creation through the object factory. */ 27 | itkTypeMacro(ShapeModelRegistrationMethodBase, Superclass); 28 | 29 | /** Constants for the image dimensions */ 30 | itkStaticConstMacro(PointDimension, unsigned int, 3U); 31 | static_assert(TShapeModel::RepresenterType::DatasetType::PointDimension == PointDimension, "Invalid dimension of input shape model. Dimension 3 is supported."); 32 | 33 | /** Typedefs of the shape model. */ 34 | typedef TShapeModel ShapeModelType; 35 | typedef TOutputMesh OutputMeshType; 36 | typedef typename ShapeModelType::RepresenterType::DatasetType DatasetType; 37 | typedef typename itk::PointSet PointSetType; 38 | 39 | /** Type of the Transform Base class */ 40 | typedef double CoordinateRepresentationType; 41 | typedef itk::LBFGSOptimizer OptimizerType; 42 | typedef itk::SingleValuedCostFunction MetricType; 43 | typedef typename itk::Optimizer::ScalesType ScalesType; 44 | typedef itk::Transform SpatialTransformType; 45 | typedef ShapeModelMultiTransform ShapeModelMultiTransformType; 46 | 47 | /** Returns the transform resulting from the registration process */ 48 | const OutputMeshType* GetOutput() const; 49 | 50 | /** Method to return the latest modified time of this object or any of its cached vars */ 51 | virtual itk::ModifiedTimeType GetMTime() const ITK_OVERRIDE; 52 | 53 | // Set logger 54 | itkSetObjectMacro(Logger, itk::Logger); 55 | 56 | // Set/Get shape model and parameters 57 | itkSetConstObjectMacro(ShapeModel, ShapeModelType); 58 | itkGetConstObjectMacro(ShapeModel, ShapeModelType); 59 | 60 | // Set/Get transform and optimizer 61 | itkSetObjectMacro(SpatialTransform, SpatialTransformType); 62 | itkGetObjectMacro(SpatialTransform, SpatialTransformType); 63 | itkGetObjectMacro(Transform, ShapeModelMultiTransformType); 64 | 65 | itkGetMacro(Scales, ScalesType); 66 | 67 | itkSetMacro(SpatialScales, ScalesType); 68 | itkGetMacro(SpatialScales, ScalesType); 69 | 70 | itkSetMacro(ModelScale, double); 71 | itkGetMacro(ModelScale, double); 72 | 73 | itkSetMacro(NumberOfIterations, size_t); 74 | itkGetMacro(NumberOfIterations, size_t); 75 | 76 | itkSetObjectMacro(Optimizer, OptimizerType); 77 | itkGetObjectMacro(Optimizer, OptimizerType); 78 | 79 | itkSetObjectMacro(Metric, MetricType); 80 | itkGetObjectMacro(Metric, MetricType); 81 | 82 | itkGetMacro(ElapsedTime, double); 83 | 84 | void SetNumberOfEpochs(const unsigned int & input) { m_NumberOfEpochs = input; m_UseNumberOfEpochs = true; }; 85 | itkGetMacro(NumberOfEpochs, unsigned int); 86 | 87 | void SetStep(const unsigned int & input) { m_Step = input; m_UseNumberOfEpochs = false; }; 88 | itkGetMacro(Step, unsigned int); 89 | 90 | void PrintReport(); 91 | 92 | protected: 93 | ShapeModelRegistrationMethodBase(); 94 | ~ShapeModelRegistrationMethodBase() {} 95 | 96 | void Initialize(); 97 | void InitializeTransform(); 98 | void InitializeOptimizer(); 99 | void ComputeMultiStageData(); 100 | void PerformRegistration(); 101 | void GenerateOutputData(); 102 | void ExceptionHandler(itk::ExceptionObject & excep, const size_t & line); 103 | 104 | typename ShapeModelType::ConstPointer m_ShapeModel; 105 | 106 | typename OptimizerType::Pointer m_Optimizer; 107 | typename OutputMeshType::Pointer m_OutputMesh; 108 | OptimizerType::ParametersType m_InitialParameters; 109 | 110 | typename MetricType::Pointer m_Metric; 111 | 112 | typename ShapeModelMultiTransformType::Pointer m_Transform; 113 | typename SpatialTransformType::Pointer m_SpatialTransform; 114 | ScalesType m_SpatialScales; 115 | ScalesType m_Scales; 116 | ScalesType m_InverseScales; 117 | double m_ModelScale; 118 | 119 | bool m_UseNumberOfEpochs; 120 | unsigned int m_NumberOfEpochs; 121 | unsigned int m_Step; 122 | itk::Array m_NumberOfUsedComponents; 123 | itk::Array m_CostFunctionValues; 124 | 125 | size_t m_NumberOfIterations; 126 | 127 | itk::TimeProbe m_Time; 128 | double m_ElapsedTime; 129 | itk::Logger::Pointer m_Logger; 130 | std::ostringstream m_Message; 131 | }; 132 | } 133 | #ifndef ITK_MANUAL_INSTANTIATION 134 | #include "ssmShapeModelRegistrationMethodBase.hxx" 135 | #endif 136 | -------------------------------------------------------------------------------- /Modules/Configs/config.ini: -------------------------------------------------------------------------------- 1 | #========================================================================= 2 | # An Example of the Configuration INI File 3 | # Statistical shape modeling 4 | # https://github.com/sMedX/StatisticalShapeModeling 5 | #========================================================================= 6 | # Author: Ruslan N. Kosarev 7 | # Date: 2017-12-20 8 | # 9 | # kosarev@smedx.com 10 | # ruslan.kosarev@gmail.com 11 | #========================================================================= 12 | 13 | # Notes: 14 | # (1) Section names should be upper-case, e.g. use 'EXTRACTION' not 'Extraction' 15 | # (2) All keys should be lower-case, e.g. use 'model' but not 'Model' 16 | 17 | [EXTRACTION] 18 | # Extract shapes from labelled binary images 19 | # Input file with a list of files of input images 20 | inplist = data/list.txt 21 | # Output file with a list of files of output shapes 22 | outlist = data/list-1-shapes.txt 23 | # Format string to format input base file name to output file name 24 | output = data/%s.vtk 25 | 26 | # Output report file 27 | report = data/report-1-extraction.csv 28 | 29 | # Sigma of the Gaussian kernel for RecursiveGaussianImageFilter to smooth input image 30 | sigma = 3 31 | # Number of iterations to adjust point positions for output shape 32 | iterations = 100 33 | # Number of points in output decimated shape (default value 0, i.e. no decimation) 34 | points = 0 35 | 36 | [ALIGNMENT] 37 | # Align shapes 38 | # Input file with a list of files of input shapes 39 | inplist = data/list-1-shapes.txt 40 | # Input file with a list of files of output shapes 41 | outlist = data/list-2-aligned.txt 42 | # Format string to format input base file name to output file name 43 | output = data/%s-aligned.vtk 44 | 45 | # Output report file 46 | report = data/report-2-alignment.csv 47 | 48 | # Final output level set image to extract reference shape 49 | reference = data/reference.nrrd 50 | 51 | # The number of stages for shape to level set image registration method, at each stage a new level set image is recomputed to continue registration 52 | stages = 5 53 | 54 | # The type of used spatial transform for shape to level set image registration method 55 | # 0 --- Translation, 56 | # 1 --- Euler3D, 57 | # 2 --- Similarity, 58 | # 3 --- ScaleSkewVersor3D 59 | transform = 2 60 | 61 | # number of iterations for shape to level set image registration method 62 | iterations = 1000 63 | 64 | [REFERENCE] 65 | # Build reference shape to establish correspondence 66 | # Input level set image 67 | input = data/reference.nrrd 68 | # Output reference shape 69 | output = data/reference.vtk 70 | 71 | # Output report file 72 | report = data/report-3-reference.csv 73 | 74 | # Sigma of the Gaussian kernel for RecursiveGaussianImageFilter to smooth input image 75 | sigma = 1 76 | 77 | # The method for shape smoothing 78 | # 0 --- None 79 | # 1 --- vtkWindowedSincPolyDataFilter 80 | # 2 --- vtkSmoothPolyDataFilter 81 | smoothing = 1 82 | 83 | # Number of iterations to adjust point positions for reference shape 84 | iterations = 100 85 | 86 | # The method for shape decimation 87 | # 0 --- None 88 | # 1 --- vtkQuadricDecimation 89 | # 2 --- vtkDecimatePro 90 | decimation = 1 91 | 92 | # The number of points in output reference shape 93 | points = 32000 94 | 95 | [CORRESPONDENCE] 96 | # Input file with a list of files of input shapes 97 | inplist = data/list-2-aligned.txt 98 | # Input file with a list of files of output shapes 99 | outlist = data/list-3-gpaligned.txt 100 | # Format string to format input base file name to output file name 101 | output = data/%s-gp.vtk 102 | 103 | # Output report file 104 | report = data/report-4-correspondence.csv 105 | 106 | # Input reference shape 107 | reference = data/reference.vtk 108 | 109 | # The number of stages 110 | stages = 5 111 | 112 | # The type of used spatial transform for shape to level set image registration method 113 | # 0 --- Translation 114 | # 1 --- Euler3D 115 | # 2 --- Similarity 116 | # 3 --- ScaleSkewVersor3D 117 | transform = 3 118 | 119 | # Number of iterations for Gaussian process model to level set image registration method 120 | iterations = 1000 121 | 122 | # Parameters to build GP model with Gaussian kernel 123 | gpmodel.scale = 50 124 | gpmodel.parameters = 30 20 10 5 125 | gpmodel.components = 100 150 150 200 126 | gpmodel.regularization = 0.10 127 | 128 | [MODELBUILDING] 129 | # Input file with a list of files of input shapes 130 | inplist = data/list-3-gpaligned.txt 131 | 132 | # Output shape model file 133 | output = data/model.h5 134 | 135 | # Mode of alignment of input shapes 136 | # GPA --- shapes rigidly are aligned to the population mean 137 | # reference --- use input reference shape 138 | mode = GPA 139 | 140 | # Input reference shape, must be specified if mode=reference 141 | reference = data/reference.vtk 142 | 143 | # Noise variance of the PCA model 144 | noise = 0 145 | 146 | [MODELQUALITY] 147 | # Perform cross validation test and compute specificity for built shape model 148 | 149 | # Input file with a list of files of input shapes 150 | inplist = data/list-3-gpaligned.txt 151 | # Input shape model file 152 | model = data/model.h5 153 | 154 | # Output report file for cross validation test 155 | cvtest.report = data/report-cross-validation.csv 156 | # Write shape corresponding to out-of-sample shapes 157 | cvtest.write = 0 158 | 159 | # Output report file for specificity 160 | specificity.report = data/report-specificity.csv 161 | # Number of random shapes to compute specificity 162 | specificity.samples = 100 163 | -------------------------------------------------------------------------------- /Applications/ssm-cross-validation.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ssmTypes.h" 10 | #include "ssmUtils.h" 11 | #include "ssmPointSetToPointSetMetrics.h" 12 | #include "ssmModelCrossValidationOptions.h" 13 | 14 | typedef statismo::DataManager DataManagerType; 15 | typedef itk::PCAModelBuilder ModelBuilderType; 16 | typedef itk::ReducedVarianceModelBuilder ReducedVarianceModelBuilderType; 17 | typedef DataManagerType::CrossValidationFoldListType CVFoldListType; 18 | typedef DataManagerType::DataItemListType DataItemListType; 19 | 20 | int main(int argc, char** argv) 21 | { 22 | // parse options 23 | ssm::ModelCrossValidationOptions options; 24 | 25 | if (!options.ParseOptions(argc, argv)) { 26 | return EXIT_FAILURE; 27 | } 28 | 29 | //---------------------------------------------------------------------------- 30 | // read list of files 31 | StringVector listOfFiles; 32 | try { 33 | listOfFiles = readListFromFile(options.GetInputList()); 34 | } 35 | catch (std::ifstream::failure & e) { 36 | std::cerr << e.what() << std::endl; 37 | return EXIT_FAILURE; 38 | } 39 | 40 | try { 41 | auto fileName = listOfFiles.begin()->c_str(); 42 | auto surface = MeshType::New(); 43 | if (!readMesh(surface, fileName)) { 44 | return EXIT_FAILURE; 45 | } 46 | 47 | // create a data manager and add a number of datasets for model building 48 | auto representer = RepresenterType::New(); 49 | representer->SetReference(surface); 50 | 51 | boost::scoped_ptr dataManager(DataManagerType::Create(representer)); 52 | 53 | for (const auto &fileName : listOfFiles) { 54 | auto surface = MeshType::New(); 55 | if (!readMesh(surface, fileName)) { 56 | return EXIT_FAILURE; 57 | } 58 | dataManager->AddDataset(surface, fileName); 59 | } 60 | 61 | CVFoldListType cvFoldList = dataManager->GetCrossValidationFolds(dataManager->GetNumberOfSamples(), true); 62 | std::cout << "number of surfaces " << dataManager->GetNumberOfSamples() << std::endl; 63 | std::cout << "number of folds " << cvFoldList.size() << std::endl; 64 | std::cout << std::endl; 65 | 66 | size_t components = options.GetNumberOfComponents(); 67 | double generalizationAbility = 0; 68 | 69 | for (const auto & it : cvFoldList) { 70 | // create the model 71 | auto modelBuilder = ModelBuilderType::New(); 72 | auto model = modelBuilder->BuildNewModel(it.GetTrainingData(), 0); 73 | 74 | // reduce the number of components 75 | auto reducedVarianceModelBuilder = ReducedVarianceModelBuilderType::New(); 76 | if (components >= 0 && components < model->GetNumberOfPrincipalComponents()) { 77 | model = reducedVarianceModelBuilder->BuildNewModelWithLeadingComponents(model, components); 78 | } 79 | 80 | std::cout << "number of samples " << it.GetTrainingData().size() << std::endl; 81 | std::cout << "number of components " << model->GetNumberOfPrincipalComponents() << std::endl; 82 | std::cout << std::endl; 83 | 84 | // Now we can iterate over the test data and do whatever validation we would like to do. 85 | const DataItemListType testSamplesList = it.GetTestingData(); 86 | 87 | for (const auto & it : testSamplesList) { 88 | auto surfaceFileName = it->GetDatasetURI(); 89 | auto testSample = it->GetSample(); 90 | auto outputSample = model->DrawSample(model->ComputeCoefficientsForDataset(testSample)); 91 | 92 | typedef std::pair PairType; 93 | std::vector info; 94 | info.push_back(PairType("components", std::to_string(model->GetNumberOfPrincipalComponents()))); 95 | 96 | // compute metrics 97 | typedef itk::PointSet PointSetType; 98 | auto pointSet1 = PointSetType::New(); 99 | pointSet1->SetPoints(testSample->GetPoints()); 100 | 101 | auto pointSet2 = PointSetType::New(); 102 | pointSet2->SetPoints(outputSample->GetPoints()); 103 | 104 | typedef ssm::PointSetToPointSetMetrics PointSetToPointSetMetricsType; 105 | auto metrics = PointSetToPointSetMetricsType::New(); 106 | metrics->SetFixedPointSet(pointSet1); 107 | metrics->SetMovingPointSet(pointSet2); 108 | metrics->SetInfo(info); 109 | metrics->Compute(); 110 | metrics->PrintReport(std::cout); 111 | metrics->PrintReportToFile(options.GetReportFileName(), getBaseNameFromPath(surfaceFileName)); 112 | 113 | generalizationAbility += metrics->GetRMSEValue(); 114 | 115 | // write samples 116 | if (options.GetWrite()) { 117 | auto suffix = "-cvtest"; 118 | auto fileName = addFileNameSuffix(surfaceFileName, suffix); 119 | if (!writeMesh(outputSample, fileName)) { 120 | return EXIT_FAILURE; 121 | } 122 | } 123 | } 124 | } 125 | 126 | generalizationAbility = generalizationAbility / dataManager->GetNumberOfSamples(); 127 | std::cout << "generalization ability " << generalizationAbility << std::endl; 128 | 129 | if (options.GetReportFileName().size() > 0) { 130 | std::ofstream file(options.GetReportFileName(), std::ofstream::out | std::ofstream::app); 131 | file << "generalization ability; " << generalizationAbility << std::endl; 132 | file.close(); 133 | } 134 | } 135 | catch (statismo::StatisticalModelException& e) { 136 | std::cerr << "Exception occurred while building the shape model" << std::endl; 137 | std::cerr << e.what() << std::endl; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Modules/SSM/ssmMeshPropertiesCalculator.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ssmMeshPropertiesCalculator.h" 9 | 10 | namespace ssm 11 | { 12 | class InvalidMeshMomentsError:public itk::ExceptionObject 13 | { 14 | public: 15 | /** 16 | * Constructor. Needed to ensure the exception object can be copied. 17 | */ 18 | InvalidMeshMomentsError(const char *file, unsigned int lineNumber):ExceptionObject(file, 19 | lineNumber) { this-> 20 | SetDescription( 21 | "No valid image moments are available."); } 22 | 23 | /** 24 | * Constructor. Needed to ensure the exception object can be copied. 25 | */ 26 | InvalidMeshMomentsError(const std::string & file, unsigned int lineNumber):ExceptionObject(file, 27 | lineNumber) { this-> 28 | SetDescription( 29 | "No valid image moments are available."); } 30 | 31 | itkTypeMacro(InvalidMeshMomentsError, ExceptionObject); 32 | }; 33 | 34 | //---------------------------------------------------------------------- 35 | // Construct without computing moments 36 | template< typename TMesh > 37 | MeshPropertiesCalculator< TMesh >::MeshPropertiesCalculator(void) 38 | { 39 | m_Valid = false; 40 | m_Mesh = ITK_NULLPTR; 41 | m_SpatialObjectMask = ITK_NULLPTR; 42 | } 43 | 44 | //---------------------------------------------------------------------- 45 | template< typename TInputImage > 46 | void MeshPropertiesCalculator< TInputImage >::PrintSelf(std::ostream & os, itk::Indent indent) const 47 | { 48 | Superclass::PrintSelf(os, indent); 49 | os << indent << "Image: " << m_Mesh.GetPointer() << std::endl; 50 | os << indent << "Valid: " << m_Valid << std::endl; 51 | } 52 | 53 | //---------------------------------------------------------------------- 54 | // Compute moments for a new or modified image 55 | template< typename TMesh > 56 | void MeshPropertiesCalculator< TMesh >::Compute() 57 | { 58 | if ( m_Mesh==nullptr ) { 59 | return; 60 | } 61 | 62 | // compute binary mask 63 | // Compute a bounding box of the input mesh 64 | typename MeshType::BoundingBoxType::ConstPointer boundingBox = m_Mesh->GetBoundingBox(); 65 | typename BinaryImageType::SpacingType spacing(1); 66 | typename BinaryImageType::PointType origin = boundingBox->GetMinimum(); 67 | typename BinaryImageType::SizeType size; 68 | 69 | for (size_t n = 0; n < Dimension; ++n) { 70 | size[n] = (boundingBox->GetMaximum()[n] - boundingBox->GetMinimum()[n]) / spacing[n]; 71 | } 72 | 73 | typedef itk::TriangleMeshToBinaryImageFilter ShapeToBinaryImageFilterType; 74 | typename ShapeToBinaryImageFilterType::Pointer shapeToImage = ShapeToBinaryImageFilterType::New(); 75 | shapeToImage->SetInput(const_cast(m_Mesh.GetPointer())); 76 | shapeToImage->SetSize(size); 77 | shapeToImage->SetOrigin(origin); 78 | shapeToImage->SetSpacing(spacing); 79 | shapeToImage->SetOutsideValue(0); 80 | shapeToImage->SetInsideValue(1); 81 | try { 82 | shapeToImage->Update(); 83 | } 84 | catch (itk::ExceptionObject& excep) { 85 | std::cout << excep << std::endl; 86 | itkExceptionMacro(<< excep); 87 | } 88 | m_Mask = shapeToImage->GetOutput(); 89 | 90 | // moment image calculator 91 | typedef itk::ImageMomentsCalculator ImageCalculatorType; 92 | typename ImageCalculatorType::Pointer m_ImageCalculator = ImageCalculatorType::New(); 93 | m_ImageCalculator->SetImage(m_Mask); 94 | try { 95 | m_ImageCalculator->Compute(); 96 | } 97 | catch (itk::ExceptionObject& excep) { 98 | std::cout << excep << std::endl; 99 | itkExceptionMacro(<< excep); 100 | } 101 | 102 | // set center of mask 103 | m_CenterOfMaskGravity = m_ImageCalculator->GetCenterOfGravity(); 104 | 105 | // compute radius 106 | m_Radius = itk::NumericTraits< ScalarType >::ZeroValue(); 107 | 108 | for (auto it = m_Mesh->GetPoints()->Begin(); it != m_Mesh->GetPoints()->End(); ++it) { 109 | typename MeshType::PointType point = it.Value(); 110 | VectorType vector; 111 | for (size_t i = 0; i < Dimension; ++i) { 112 | vector[i] = point[i] - m_CenterOfMaskGravity[i]; 113 | } 114 | m_Radius += vector.GetNorm(); 115 | } 116 | 117 | m_Radius /= m_Mesh->GetNumberOfPoints(); 118 | 119 | /* Remember that the moments are valid */ 120 | m_Valid = 1; 121 | } 122 | 123 | //-------------------------------------------------------------------- 124 | // Get center of mask gravity, in physical coordinates 125 | template< typename TMesh > 126 | typename MeshPropertiesCalculator< TMesh >::VectorType MeshPropertiesCalculator< TMesh >::GetCenterOfMaskGravity() const 127 | { 128 | if (!m_Valid) { 129 | itkExceptionMacro(<< "GetCenterOfGravity() invoked, but the moments have not been computed. Call Compute() first."); 130 | } 131 | return m_CenterOfMaskGravity; 132 | } 133 | //-------------------------------------------------------------------- 134 | // Get center of radius 135 | template< typename TMesh > 136 | typename MeshPropertiesCalculator< TMesh >::ScalarType MeshPropertiesCalculator< TMesh >::GetRadius() const 137 | { 138 | if (!m_Valid) { 139 | itkExceptionMacro(<< "GetCenterOfGravity() invoked, but the moments have not been computed. Call Compute() first."); 140 | } 141 | return m_Radius; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Applications/ssm-extract-surface.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "ssmTypes.h" 6 | #include "ssmUtils.h" 7 | #include "ssmPointSetToImageMetrics.h" 8 | #include "ssmBinaryImageToLevelSetImageFilter.h" 9 | #include "ssmImage3DMeshSource.h" 10 | #include "ssmExtractionOptions.h" 11 | 12 | double averageLengthOfEdges(vtkPolyData* poly); 13 | double averageAreaOfCells(vtkPolyData* poly); 14 | bool extractSurface(const std::string & inputFileName, const std::string & outputFileName, const ssm::ExtractionOptions & options); 15 | 16 | int main(int argc, char** argv) 17 | { 18 | // parse options 19 | ssm::ExtractionOptions options; 20 | 21 | if (!options.ParseOptions(argc, argv)) { 22 | return EXIT_FAILURE; 23 | } 24 | 25 | if ( !options.ConfigIsEnabled() ) { 26 | if (!extractSurface(options.GetInputFileName(), options.GetOutputFileName(), options)) { 27 | return EXIT_FAILURE; 28 | } 29 | return EXIT_SUCCESS; 30 | } 31 | 32 | //---------------------------------------------------------------------------- 33 | // read list of files 34 | StringVector listOfInputFiles; 35 | try { 36 | listOfInputFiles = readListFromFile(options.GetInputList()); 37 | } 38 | catch (std::ifstream::failure & e) { 39 | std::cout << e.what() << std::endl; 40 | return EXIT_FAILURE; 41 | } 42 | 43 | // extract surfaces 44 | StringVector listOfOutputFiles; 45 | 46 | for (const auto & inputFileName : listOfInputFiles) { 47 | std::string outputFileName = options.FormatOutput(inputFileName); 48 | 49 | if(!extractSurface(inputFileName, outputFileName, options)) { 50 | return EXIT_FAILURE; 51 | } 52 | 53 | listOfOutputFiles.push_back(outputFileName); 54 | } 55 | 56 | // write list of files 57 | try { 58 | writeListToFile(options.GetOutputList(), listOfOutputFiles); 59 | } 60 | catch (std::ofstream::failure & e) { 61 | std::cout << e.what() << std::endl; 62 | return EXIT_FAILURE; 63 | } 64 | 65 | return EXIT_SUCCESS; 66 | } 67 | 68 | bool extractSurface(const std::string & inputFileName, const std::string & outputFileName, const ssm::ExtractionOptions & options ) 69 | { 70 | // read image 71 | auto image = BinaryImageType::New(); 72 | if (!readImage(image, inputFileName)) { 73 | return false; 74 | } 75 | printImageInfo(image, inputFileName); 76 | 77 | typedef ssm::Image3DMeshSource Image3DMeshSourceType; 78 | auto binaryMaskToSurface = Image3DMeshSourceType::New(); 79 | binaryMaskToSurface->SetInput(image); 80 | try { 81 | binaryMaskToSurface->SetSigma(options.GetSigma()); 82 | binaryMaskToSurface->SetNumberOfIterations(options.GetNumberOfIterations()); 83 | binaryMaskToSurface->SetNumberOfPoints(options.GetNumberOfPoints()); 84 | } 85 | catch (...) { 86 | return false; 87 | } 88 | 89 | try { 90 | binaryMaskToSurface->Update(); 91 | } 92 | catch (itk::ExceptionObject& excep) { 93 | std::cerr << excep << std::endl; 94 | return false; 95 | } 96 | auto surface = binaryMaskToSurface->GetOutput(); 97 | 98 | // write polydata to the file 99 | if (!writeVTKPolydata(surface, outputFileName)) { 100 | return false; 101 | } 102 | 103 | //---------------------------------------------------------------------------- 104 | // compute report 105 | typedef std::pair PairType; 106 | std::vector surfaceInfo; 107 | surfaceInfo.push_back(PairType("number of points", std::to_string(surface->GetNumberOfPoints()))); 108 | surfaceInfo.push_back(PairType(" number of cells", std::to_string(surface->GetNumberOfCells()))); 109 | surfaceInfo.push_back(PairType(" length of edges", std::to_string(averageLengthOfEdges(surface)))); 110 | surfaceInfo.push_back(PairType(" area of cells", std::to_string(averageAreaOfCells(surface)))); 111 | 112 | //---------------------------------------------------------------------------- 113 | // compute level set image 114 | typedef ssm::BinaryImageToLevelSetImageFilter BinaryImageToLevelSetImageType; 115 | auto binaryImageToLevelset = BinaryImageToLevelSetImageType::New(); 116 | binaryImageToLevelset->SetInput(image); 117 | 118 | // compute metrics 119 | typedef itk::PointSet PointSetType; 120 | typedef ssm::PointSetToImageMetrics PointSetToImageMetricsType; 121 | auto metrics = PointSetToImageMetricsType::New(); 122 | metrics->SetPointSetAsPolyData(surface); 123 | metrics->SetImage(binaryImageToLevelset->GetOutput()); 124 | metrics->SetInfo(surfaceInfo); 125 | try { 126 | metrics->Compute(); 127 | } 128 | catch (itk::ExceptionObject& excep) { 129 | std::cerr << excep << std::endl; 130 | return false; 131 | } 132 | 133 | metrics->PrintReport(); 134 | metrics->PrintReportToFile(options.GetReportFileName(), getBaseNameFromPath(options.GetOutputFileName())); 135 | 136 | return true; 137 | } 138 | 139 | double averageLengthOfEdges(vtkPolyData*poly) 140 | { 141 | const unsigned int numberOfCells = poly->GetNumberOfCells(); 142 | double sum = 0; 143 | 144 | for (int n = 0; n < poly->GetNumberOfCells(); ++n) { 145 | double p1[3], p2[3], p3[3]; 146 | 147 | vtkSmartPointer cell = poly->GetCell(n); 148 | vtkSmartPointer points = cell->GetPoints(); 149 | 150 | points->GetPoint(0, p1); 151 | points->GetPoint(1, p2); 152 | points->GetPoint(2, p3); 153 | 154 | sum += sqrt(vtkMath::Distance2BetweenPoints(p1, p2)) + 155 | sqrt(vtkMath::Distance2BetweenPoints(p1, p3)) + 156 | sqrt(vtkMath::Distance2BetweenPoints(p2, p3)); 157 | } 158 | 159 | return sum / (3 * numberOfCells); 160 | } 161 | 162 | double averageAreaOfCells(vtkPolyData*poly) 163 | { 164 | const size_t numberOfCells = poly->GetNumberOfCells(); 165 | double sum = 0; 166 | 167 | for (size_t n = 0; n < numberOfCells; ++n) { 168 | double a[3], b[3], c[3]; 169 | double p1[3], p2[3], p3[3]; 170 | 171 | vtkSmartPointer cell = poly->GetCell(n); 172 | vtkSmartPointer points = cell->GetPoints(); 173 | 174 | points->GetPoint(0, p1); 175 | points->GetPoint(1, p2); 176 | points->GetPoint(2, p3); 177 | 178 | vtkMath::Subtract(p1, p2, a); 179 | vtkMath::Subtract(p1, p3, b); 180 | vtkMath::Cross(a, b, c); 181 | 182 | sum += sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]) / 2; 183 | } 184 | 185 | return sum / numberOfCells; 186 | } 187 | -------------------------------------------------------------------------------- /Modules/SSM/ssmShapeModelMultiTransform.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ssmShapeModelMultiTransform.h" 6 | 7 | namespace ssm 8 | { 9 | 10 | template 11 | ShapeModelMultiTransform 12 | ::ShapeModelMultiTransform():Superclass(0) 13 | { 14 | m_ShapeModel = nullptr; 15 | m_SpatialTransform = itk::IdentityTransform::New(); 16 | 17 | m_NumberOfComponents = 0; 18 | m_NumberOfParameters = 0; 19 | m_NumberOfUsedComponents = 0; 20 | } 21 | 22 | template 23 | void ShapeModelMultiTransform::SetParameters(const ParametersType & parameters) 24 | { 25 | /* Verify proper input size. */ 26 | if (parameters.Size() != this->GetNumberOfParameters()) { 27 | itkExceptionMacro(<< "Input parameter list size is not expected size. " << parameters.Size() << " instead of " << this->GetNumberOfParameters() << "."); 28 | } 29 | 30 | for (size_t i = 0; i < m_NumberOfUsedComponents; ++i) { 31 | m_ShapeModelParameters[i] = parameters[i]; 32 | } 33 | 34 | for (size_t i = m_NumberOfUsedComponents; i < m_NumberOfComponents; ++i) { 35 | m_ShapeModelParameters[i] = 0; 36 | } 37 | 38 | /* set parameters to spatial transform */ 39 | const auto parameterSize = m_SpatialTransform->GetParameters().Size(); 40 | m_SpatialTransform->CopyInParameters(&(parameters.data_block())[m_NumberOfComponents], &(parameters.data_block())[m_NumberOfComponents] + parameterSize); 41 | 42 | this->Modified(); 43 | } 44 | 45 | 46 | template 47 | const typename ShapeModelMultiTransform::ParametersType & 48 | ShapeModelMultiTransform::GetParameters() const 49 | { 50 | this->m_Parameters.SetSize(this->GetNumberOfParameters()); 51 | 52 | /* use vnl_vector data_block() to get data ptr */ 53 | std::copy(m_ShapeModelParameters.data_block(), m_ShapeModelParameters.data_block() + m_NumberOfUsedComponents, &(this->m_Parameters.data_block())[0]); 54 | 55 | for (size_t i = m_NumberOfUsedComponents; i < m_NumberOfComponents; ++i) { 56 | this->m_Parameters[i] = 0; 57 | } 58 | 59 | /* use vnl_vector data_block() to get data ptr */ 60 | const ParametersType & subParameters = m_SpatialTransform->GetParameters(); 61 | std::copy(subParameters.data_block(), subParameters.data_block() + subParameters.Size(), &(this->m_Parameters.data_block())[m_NumberOfComponents]); 62 | 63 | return this->m_Parameters; 64 | } 65 | 66 | 67 | template 68 | void ShapeModelMultiTransform::PrintSelf(std::ostream & os, itk::Indent indent) const 69 | { 70 | Superclass::PrintSelf(os, indent); 71 | } 72 | 73 | template 74 | typename ShapeModelMultiTransform::OutputPointType 75 | ShapeModelMultiTransform::TransformPoint(const size_t & index) const 76 | { 77 | try { 78 | return m_SpatialTransform->TransformPoint(m_ShapeModel->DrawSampleAtPoint(m_ShapeModelParameters, index)); 79 | } 80 | catch (itk::ExceptionObject &excep) { 81 | std::cout << "exception occurred at point " << index << std::endl; 82 | itkExceptionMacro(<< excep); 83 | } 84 | } 85 | 86 | template 87 | typename ShapeModelMultiTransform::OutputPointType 88 | ShapeModelMultiTransform::TransformPoint(const InputPointType & p) const 89 | { 90 | try { 91 | return m_SpatialTransform->TransformPoint(m_ShapeModel->DrawSampleAtPoint(m_ShapeModelParameters, p)); 92 | } 93 | catch (itk::ExceptionObject &excep) { 94 | std::cout << "exception occurred at point " << p << std::endl; 95 | itkExceptionMacro(<< excep); 96 | } 97 | } 98 | 99 | 100 | template 101 | void ShapeModelMultiTransform 102 | ::ComputeJacobianWithRespectToParameters( const InputPointType & p, JacobianType & outJacobian) const 103 | { 104 | /* Returns a concatenated MxN array, holding the Jacobian of each sub 105 | * transform that is selected for optimization. 106 | * M rows = dimensionality of the transforms 107 | * N cols = total number of parameters in the selected sub transforms. */ 108 | outJacobian.SetSize(Dimension, m_NumberOfParameters); 109 | JacobianType jacobianWithRespectToPosition(Dimension, Dimension); 110 | this->ComputeJacobianWithRespectToParametersCachedTemporaries(p, outJacobian, jacobianWithRespectToPosition); 111 | } 112 | 113 | template 114 | void ShapeModelMultiTransform 115 | ::ComputeJacobianWithRespectToParametersCachedTemporaries(const InputPointType & p, JacobianType & outJacobian, JacobianType & jacobianWithRespectToPosition) const 116 | { 117 | outJacobian.set_size(Dimension, m_NumberOfParameters); 118 | JacobianType modelJacobian(Dimension, m_NumberOfComponents); 119 | JacobianType spatialJacobian(Dimension, this->m_SpatialTransform->GetNumberOfParameters()); 120 | 121 | const auto & index = m_ShapeModel->GetRepresenter()->GetPointIdForPoint(p); 122 | 123 | this->ComputeJacobianWithRespectToParametersCachedTemporaries(index, outJacobian, modelJacobian, spatialJacobian, jacobianWithRespectToPosition); 124 | } 125 | 126 | template 127 | void ShapeModelMultiTransform 128 | ::ComputeJacobianWithRespectToParametersCachedTemporaries(const size_t & index, JacobianType & outJacobian, JacobianType & modelJacobian, JacobianType & spatialJacobian, JacobianType & jacobianWithRespectToPosition) const 129 | { 130 | const auto & jacobian = m_ShapeModel->GetJacobian(index); 131 | 132 | for (size_t i = 0; i < Dimension; ++i) { 133 | for (size_t k = 0; k < m_NumberOfUsedComponents; ++k) { 134 | modelJacobian[i][k] = jacobian[i][k]; 135 | } 136 | 137 | for (size_t k = m_NumberOfUsedComponents; k < m_NumberOfComponents; ++k) { 138 | modelJacobian[i][k] = 0; 139 | } 140 | } 141 | 142 | const auto & modelTransformedPoint = m_ShapeModel->DrawSampleAtPoint(m_ShapeModelParameters, index); 143 | 144 | m_SpatialTransform->ComputeJacobianWithRespectToParameters(modelTransformedPoint, spatialJacobian); 145 | outJacobian.update(spatialJacobian, 0, m_NumberOfComponents); 146 | 147 | m_SpatialTransform->ComputeJacobianWithRespectToPosition(modelTransformedPoint, jacobianWithRespectToPosition); 148 | outJacobian.update(jacobianWithRespectToPosition * modelJacobian, 0, 0); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Modules/SSM/ssmShapeModelMultiTransform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace ssm 7 | { 8 | template 9 | class ShapeModelMultiTransform : public itk::Transform 10 | { 11 | public: 12 | /** Standard class typedefs. */ 13 | typedef ShapeModelMultiTransform Self; 14 | typedef itk::Transform Superclass; 15 | typedef itk::SmartPointer Pointer; 16 | typedef itk::SmartPointer ConstPointer; 17 | 18 | /** New macro for creation of through the object factory. */ 19 | itkNewMacro(Self); 20 | 21 | /** Run-time type information (and related methods). */ 22 | itkTypeMacro(ShapeModelMultiTransform, Transform); 23 | 24 | /** Dimension of the domain space. */ 25 | itkStaticConstMacro(Dimension, unsigned int, TDataset::PointDimension); 26 | 27 | /** Standard scalar type for this class. */ 28 | typedef TDataset DatasetType; 29 | typedef itk::StatisticalModel ShapeModelType; 30 | typedef itk::Transform SpatialTransformType; 31 | 32 | /** Standard parameters container. */ 33 | typedef TParametersValueType ParametersValueType; 34 | typedef typename Superclass::ParametersType ParametersType; 35 | typedef typename Superclass::FixedParametersType FixedParametersType; 36 | typedef typename ShapeModelType::VectorType ShapeModelParametersType; 37 | 38 | /** Standard Jacobian container. */ 39 | typedef typename Superclass::JacobianType JacobianType; 40 | 41 | /** The number of parameters defining this transform. */ 42 | typedef typename Superclass::NumberOfParametersType NumberOfParametersType; 43 | 44 | /** Standard coordinate point type for this class. */ 45 | typedef typename Superclass::InputPointType InputPointType; 46 | typedef typename Superclass::OutputPointType OutputPointType; 47 | 48 | /** This method sets the parameters for the transform value specified by the user. */ 49 | virtual void SetParameters(const ParametersType & parameters) ITK_OVERRIDE; 50 | 51 | /** Get the Transformation Parameters. */ 52 | virtual const ParametersType & GetParameters() const ITK_OVERRIDE; 53 | 54 | /** Transform point. */ 55 | OutputPointType TransformPoint(const InputPointType & point) const ITK_OVERRIDE; 56 | OutputPointType TransformPoint(const size_t & index) const; 57 | 58 | /** Compute the Jacobian Matrix of the transformation at one point */ 59 | virtual void ComputeJacobianWithRespectToParameters(const InputPointType & point, JacobianType & j) const ITK_OVERRIDE; 60 | 61 | /** 62 | * Expanded interface to Compute the Jacobian with respect to the parameters for the composite transform using Jacobian rule. This version takes in temporary 63 | * variables to avoid excessive constructions. NOTE: outJacobian and jacobianWithRespectToPosition MUST be sized 64 | * prior to the call; outJacobian's size should be [NDimensions, this->GetNumberOfLocalParameters() ] 65 | * jacobianWithRespectToPosition size == [ NDimensions, NDimensions ] 66 | */ 67 | virtual void ComputeJacobianWithRespectToParametersCachedTemporaries(const InputPointType & p, JacobianType & outJacobian, JacobianType & jacobianWithRespectToPosition) const ITK_OVERRIDE; 68 | virtual void ComputeJacobianWithRespectToParametersCachedTemporaries(const size_t & index, JacobianType & outJacobian, JacobianType & modelJacobian, JacobianType & spatialJacobian, JacobianType & jacobianWithRespectToPosition) const; 69 | 70 | /** Return the number of parameters that completely define the Transform */ 71 | virtual NumberOfParametersType GetNumberOfParameters() const ITK_OVERRIDE 72 | { 73 | return m_NumberOfParameters; 74 | } 75 | 76 | /** Set the fixed parameters and update internal transformation. */ 77 | virtual void SetFixedParameters(const FixedParametersType &) ITK_OVERRIDE 78 | { 79 | itkExceptionMacro(<< "method SetFixedParametersType is not implemented"); 80 | } 81 | 82 | /** Get the Fixed Parameters. */ 83 | virtual const FixedParametersType & GetFixedParameters() const ITK_OVERRIDE 84 | { 85 | itkExceptionMacro(<<"method GetFixedParametersType is not implemented"); 86 | } 87 | 88 | itkGetConstReferenceMacro(NumberOfUsedComponents, NumberOfParametersType); 89 | itkGetConstReferenceMacro(NumberOfComponents, NumberOfParametersType); 90 | 91 | virtual void SetNumberOfUsedComponents(const NumberOfParametersType arg) 92 | { 93 | if (!m_ShapeModel) { 94 | itkExceptionMacro(<< "Number of used components must be initialized after shape model.") 95 | } 96 | 97 | if (m_NumberOfUsedComponents != arg) { 98 | m_NumberOfUsedComponents = arg; 99 | this->Modified(); 100 | } 101 | } 102 | 103 | /**Set/Get methods for spatial transform */ 104 | itkGetConstObjectMacro(SpatialTransform, SpatialTransformType); 105 | void SetSpatialTransform(SpatialTransformType * arg) 106 | { 107 | itkDebugMacro("setting m_SpatialTransform to " << arg); 108 | if (m_SpatialTransform != arg) { 109 | m_SpatialTransform = arg; 110 | m_NumberOfParameters = this->m_SpatialTransform->GetNumberOfParameters() + m_NumberOfComponents; 111 | this->Modified(); 112 | } 113 | } 114 | 115 | /**Set/Get methods for shape model*/ 116 | itkGetConstObjectMacro(ShapeModel, ShapeModelType); 117 | void SetShapeModel(const ShapeModelType * arg) 118 | { 119 | itkDebugMacro("setting m_ShapeModel to " << arg); 120 | if (m_ShapeModel != arg) { 121 | m_ShapeModel = arg; 122 | m_NumberOfComponents = m_ShapeModel->GetNumberOfPrincipalComponents(); 123 | m_NumberOfParameters = m_SpatialTransform->GetNumberOfParameters() + m_NumberOfComponents; 124 | 125 | m_NumberOfUsedComponents = m_NumberOfComponents; 126 | m_ShapeModelParameters.set_size(m_NumberOfUsedComponents); 127 | m_ShapeModelParameters.fill(0); 128 | 129 | this->Modified(); 130 | } 131 | } 132 | 133 | protected: 134 | ShapeModelMultiTransform(); 135 | ~ShapeModelMultiTransform() {}; 136 | /** Print contents of an TranslationTransform. */ 137 | virtual void PrintSelf(std::ostream & os, itk::Indent indent) const ITK_OVERRIDE; 138 | 139 | private: 140 | ShapeModelMultiTransform(const Self &) ITK_DELETE_FUNCTION; 141 | void operator=(const Self &) ITK_DELETE_FUNCTION; 142 | 143 | typename ShapeModelType::ConstPointer m_ShapeModel; 144 | typename SpatialTransformType::Pointer m_SpatialTransform; 145 | 146 | ShapeModelParametersType m_ShapeModelParameters; 147 | NumberOfParametersType m_NumberOfComponents; 148 | NumberOfParametersType m_NumberOfParameters; 149 | NumberOfParametersType m_NumberOfUsedComponents; 150 | }; 151 | } 152 | 153 | #ifndef ITK_MANUAL_INSTANTIATION 154 | #include "ssmShapeModelMultiTransform.hxx" 155 | #endif 156 | -------------------------------------------------------------------------------- /Modules/SSM/ssmImage3DMeshSource.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ssmImage3DMeshSource.h" 16 | 17 | namespace ssm 18 | { 19 | template< typename TInputImage, typename TOutputMesh > 20 | Image3DMeshSource< TInputImage, TOutputMesh>::Image3DMeshSource() 21 | { 22 | m_Sigma = 0; 23 | 24 | m_Smoothing = Smoothing::WindowedSinc; 25 | m_NumberOfIterations = 100; 26 | m_RelaxationFactor = 0.2; 27 | m_FeatureAngle = 60.0; 28 | m_PassBand = 0.001; 29 | 30 | m_Decimation = Decimation::None; 31 | m_NumberOfPoints = 0; 32 | 33 | m_ComputeLevelValue = true; 34 | } 35 | 36 | 37 | template< typename TInputImage, typename TOutputMesh > 38 | void Image3DMeshSource< TInputImage, TOutputMesh >::SetInput(const TInputImage *image) 39 | { 40 | this->ProcessObject::SetNthInput( 0, const_cast< InputImageType * >( image ) ); 41 | } 42 | 43 | template< typename TInputImage, typename TOutputMesh > 44 | typename TInputImage::ConstPointer Image3DMeshSource::GetInput() 45 | { 46 | return static_cast(this->ProcessObject::GetInput(0)); 47 | } 48 | 49 | template< typename TInputImage, typename TOutputMesh > 50 | TOutputMesh* Image3DMeshSource::GetOutput() 51 | { 52 | return m_Output; 53 | } 54 | 55 | /** Generate the data */ 56 | template< typename TInputImage, typename TOutputMesh > 57 | void Image3DMeshSource< TInputImage, TOutputMesh >::GenerateData() 58 | { 59 | // compute the minimum and the maximum intensity values of label 60 | if (m_ComputeLevelValue) { 61 | typedef itk::MinimumMaximumImageCalculator MinimumMaximumImageCalculatorType; 62 | auto labelValues = MinimumMaximumImageCalculatorType::New(); 63 | labelValues->SetImage(this->GetInput()); 64 | labelValues->Compute(); 65 | m_LevelValue = 0.5 * (labelValues->GetMinimum() + labelValues->GetMaximum()); 66 | } 67 | 68 | // smoothing 69 | if (std::abs(m_Sigma) < itk::NumericTraits::epsilon()) { 70 | m_Sigma = this->GetInput()->GetSpacing().GetVnlVector().max_value(); 71 | } 72 | 73 | typedef itk::RecursiveGaussianImageFilter RecursiveGaussianImageFilterType; 74 | auto gaussian = RecursiveGaussianImageFilterType::New(); 75 | gaussian->SetInput(this->GetInput()); 76 | gaussian->SetSigma(m_Sigma); 77 | 78 | // fill holes after smoothing 79 | typedef itk::GrayscaleFillholeImageFilter GrayscaleFillholeImageFilterType; 80 | auto fillholes = GrayscaleFillholeImageFilterType::New(); 81 | fillholes->SetInput(gaussian->GetOutput()); 82 | fillholes->SetFullyConnected(true); 83 | fillholes->Update(); 84 | 85 | // convert ITK image to VTK image 86 | typedef itk::ImageToVTKImageFilter ConvertorType; 87 | ConvertorType::Pointer convertor = ConvertorType::New(); 88 | convertor->SetInput(fillholes->GetOutput()); 89 | convertor->Update(); 90 | 91 | // extract surface 92 | typedef vtkSmartPointer MarchingCubes; 93 | auto mcubes = MarchingCubes::New(); 94 | mcubes->SetInputData(convertor->GetOutput()); 95 | mcubes->SetValue(0, m_LevelValue); 96 | mcubes->Update(); 97 | m_Output = mcubes->GetOutput(); 98 | 99 | // decimate surface 100 | this->SurfaceDecimation(); 101 | 102 | // smoothing surface 103 | this->SurfaceSmoothing(); 104 | 105 | // compute normals 106 | typedef vtkSmartPointer PolyDataNormals; 107 | auto normals = PolyDataNormals::New(); 108 | normals->SetInputData(m_Output); 109 | normals->AutoOrientNormalsOn(); 110 | normals->FlipNormalsOff(); 111 | normals->ConsistencyOn(); 112 | normals->ComputeCellNormalsOff(); 113 | normals->SplittingOff(); 114 | normals->Update(); 115 | 116 | m_Output = normals->GetOutput(); 117 | } 118 | 119 | /** Decimate surface */ 120 | template< typename TInputImage, typename TOutputMesh > 121 | void Image3DMeshSource< TInputImage, TOutputMesh >::SurfaceDecimation() 122 | { 123 | // decimation 124 | if (m_Decimation==Decimation::None || m_NumberOfPoints == 0) { 125 | return; 126 | } 127 | 128 | while (m_Output->GetNumberOfPoints() > m_NumberOfPoints) { 129 | m_Reduction = (m_Output->GetNumberOfPoints() - m_NumberOfPoints) / (double)m_Output->GetNumberOfPoints(); 130 | 131 | switch (m_Decimation) { 132 | case Decimation::QuadricDecimation: { 133 | typedef vtkSmartPointer Decimation; 134 | auto decimate = Decimation::New(); 135 | decimate->SetInputData(m_Output); 136 | decimate->SetTargetReduction(m_Reduction); 137 | decimate->Update(); 138 | m_Output = decimate->GetOutput(); 139 | break; 140 | } 141 | case Decimation::DecimatePro: { 142 | typedef vtkSmartPointer Decimate; 143 | auto decimate = Decimate::New(); 144 | decimate->SetInputData(m_Output); 145 | decimate->SetSplitting(false); 146 | decimate->SetErrorIsAbsolute(5); 147 | decimate->SetFeatureAngle(m_FeatureAngle); 148 | decimate->SetPreserveTopology(true); 149 | decimate->SetBoundaryVertexDeletion(false); 150 | decimate->SetDegree(10); // std-value is 25! 151 | decimate->SetTargetReduction(m_Reduction); 152 | decimate->SetMaximumError(0.002); 153 | decimate->Update(); 154 | m_Output = decimate->GetOutput(); 155 | break; 156 | } 157 | } 158 | } 159 | } 160 | 161 | /** Decimate surface */ 162 | template< typename TInputImage, typename TOutputMesh > 163 | void Image3DMeshSource< TInputImage, TOutputMesh >::SurfaceSmoothing() 164 | { 165 | // smoothing 166 | switch (m_Smoothing) { 167 | case Smoothing::Laplacian: { 168 | typedef vtkSmartPointer SmoothPolyData; 169 | auto smoother = SmoothPolyData::New(); 170 | smoother->SetInputData(m_Output); 171 | smoother->SetNumberOfIterations(m_NumberOfIterations); 172 | smoother->SetRelaxationFactor(m_RelaxationFactor); 173 | smoother->SetFeatureAngle(m_FeatureAngle); 174 | smoother->SetConvergence(0); 175 | smoother->SetBoundarySmoothing(false); 176 | smoother->SetFeatureEdgeSmoothing(false); 177 | smoother->Update(); 178 | m_Output = smoother->GetOutput(); 179 | break; 180 | } 181 | case Smoothing::WindowedSinc: { 182 | typedef vtkSmartPointer SmoothPolyData; 183 | auto smoother = SmoothPolyData::New(); 184 | smoother->SetInputData(m_Output); 185 | smoother->SetNumberOfIterations(m_NumberOfIterations); 186 | smoother->SetFeatureEdgeSmoothing(false); 187 | smoother->SetFeatureAngle(m_FeatureAngle); 188 | smoother->SetPassBand(m_PassBand); 189 | smoother->SetNonManifoldSmoothing(true); 190 | smoother->SetNormalizeCoordinates(true); 191 | smoother->SetBoundarySmoothing(false); 192 | smoother->Update(); 193 | m_Output = smoother->GetOutput(); 194 | break; 195 | } 196 | } 197 | 198 | } 199 | 200 | /** Print report */ 201 | template< typename TInputImage, typename TOutputMesh > 202 | void Image3DMeshSource< TInputImage, TOutputMesh >::PrintReport() const 203 | { 204 | 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /Modules/SSM/ssmShapeModelToLevelSetImageMetric.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace ssm 12 | { 13 | template 14 | class ShapeModelToLevelSetImageMetric:public itk::SingleValuedCostFunction 15 | { 16 | public: 17 | /** Standard class typedefs. */ 18 | typedef ShapeModelToLevelSetImageMetric Self; 19 | typedef itk::SingleValuedCostFunction Superclass; 20 | typedef itk::SmartPointer Pointer; 21 | typedef itk::SmartPointer ConstPointer; 22 | 23 | /** Method for creation through the object factory. */ 24 | itkNewMacro(Self); 25 | 26 | /** Run-time type information (and related methods). */ 27 | itkTypeMacro(ShapeModelToLevelSetImageMetric, itk::Object); 28 | 29 | /** Type used for representing point components */ 30 | typedef Superclass::ParametersValueType CoordinateRepresentationType; 31 | 32 | /** Type of the moving Image. */ 33 | typedef TImage ImageType; 34 | typedef typename TImage::PixelType ImagePixelType; 35 | typedef typename ImageType::ConstPointer ImageConstPointer; 36 | 37 | /** Type of the fixed Image. */ 38 | typedef typename TShapeModel::RepresenterType RepresenterType; 39 | typedef typename RepresenterType::DatasetType DatasetType; 40 | typedef typename itk::PointSet PointSetType; 41 | typedef typename PointSetType::PointsContainer PointsContainerType; 42 | typedef typename PointSetType::ConstPointer PointSetConstPointer; 43 | typedef typename PointSetType::PointsContainer::ConstIterator PointIteratorType; 44 | 45 | /** Constants for the image dimensions */ 46 | itkStaticConstMacro(ImageDimension, unsigned int, TImage::ImageDimension); 47 | itkStaticConstMacro(PointDimension, unsigned int, TShapeModel::RepresenterType::DatasetType::PointDimension); 48 | 49 | /** Type of the Transform Base class */ 50 | typedef TTransform TransformType; 51 | typedef typename TransformType::Pointer TransformPointer; 52 | typedef typename TransformType::InputPointType InputPointType; 53 | typedef typename TransformType::OutputPointType OutputPointType; 54 | typedef typename TransformType::ParametersType TransformParametersType; 55 | typedef typename TransformType::JacobianType TransformJacobianType; 56 | 57 | /** Type of the Interpolator Base class */ 58 | typedef itk::InterpolateImageFunction InterpolatorType; 59 | 60 | /** Gaussian filter to compute the gradient of the Moving Image */ 61 | typedef typename itk::NumericTraits< ImagePixelType >::RealType RealType; 62 | typedef itk::CovariantVector GradientPixelType; 63 | typedef itk::Image GradientImageType; 64 | typedef typename itk::GradientRecursiveGaussianImageFilter GradientImageFilterType; 65 | typedef typename GradientImageFilterType::Pointer GradientImageFilterPointer; 66 | typedef typename InterpolatorType::Pointer InterpolatorPointer; 67 | 68 | /** Type of the measure. */ 69 | typedef Superclass::MeasureType MeasureType; 70 | 71 | /** Type of the derivative. */ 72 | typedef Superclass::DerivativeType DerivativeType; 73 | 74 | /** Type of the parameters. */ 75 | typedef Superclass::ParametersType ParametersType; 76 | 77 | // Set logger 78 | itkSetObjectMacro(Logger, itk::Logger); 79 | 80 | /** Get/Set the shape model. */ 81 | itkSetConstObjectMacro(ShapeModel, TShapeModel); 82 | itkGetConstObjectMacro(ShapeModel, TShapeModel); 83 | 84 | /** Get/Set the Moving Image. */ 85 | itkSetConstObjectMacro(LevelSetImage, ImageType); 86 | itkGetConstObjectMacro(LevelSetImage, ImageType); 87 | 88 | /** Connect the Transform. */ 89 | itkSetObjectMacro(Transform, TransformType); 90 | 91 | /** Get a pointer to the Transform. */ 92 | itkGetModifiableObjectMacro(Transform, TransformType); 93 | 94 | /** Connect the Interpolator. */ 95 | itkSetObjectMacro(Interpolator, InterpolatorType); 96 | 97 | /** Get a pointer to the Interpolator. */ 98 | itkGetModifiableObjectMacro(Interpolator, InterpolatorType); 99 | 100 | /** Get Gradient Image. */ 101 | itkGetModifiableObjectMacro(GradientImage, GradientImageType); 102 | 103 | /** Get the number of pixels considered in the computation. */ 104 | itkGetConstReferenceMacro(NumberOfSamplesCounted, itk::SizeValueType); 105 | 106 | itkSetMacro(RegularizationParameter, double); 107 | itkGetMacro(RegularizationParameter, double); 108 | 109 | itkSetMacro(Degree, size_t); 110 | itkGetMacro(Degree, size_t); 111 | 112 | itkSetMacro(NumberOfThreads, size_t); 113 | itkGetMacro(NumberOfThreads, size_t); 114 | itkGetMacro(MaximalNumberOfThreads, size_t); 115 | 116 | itkSetMacro(NumberOfUsedPoints, size_t); 117 | itkGetMacro(NumberOfUsedPoints, size_t); 118 | 119 | itkGetMacro(IsInitialized, bool); 120 | 121 | /** Set/Get the flag for computing the image gradient */ 122 | itkSetMacro(ComputeGradient, bool); 123 | itkGetConstReferenceMacro(ComputeGradient, bool); 124 | 125 | /** Return the number of parameters required by the Transform */ 126 | virtual unsigned int GetNumberOfParameters(void) const ITK_OVERRIDE {return m_NumberOfParameters;} 127 | 128 | /** Initialize the Metric by making sure that all the components are present and plugged together correctly */ 129 | virtual void Initialize(void) 130 | throw(itk::ExceptionObject); 131 | 132 | /** Get the derivatives of the match measure. */ 133 | void GetDerivative(const TransformParametersType & parameters, DerivativeType & Derivative) const ITK_OVERRIDE; 134 | 135 | /** Get the value for single valued optimizers. */ 136 | MeasureType GetValue(const TransformParametersType & parameters) const ITK_OVERRIDE; 137 | 138 | /** Get value and derivatives for multiple valued optimizers. */ 139 | void GetValueAndDerivative(const TransformParametersType & parameters, MeasureType & Value, DerivativeType & Derivative) const ITK_OVERRIDE; 140 | 141 | /**Compute penalty. */ 142 | void CalculateValueAndDerivativePenalty(const TransformParametersType & parameters, MeasureType & value, DerivativeType & derivative) const; 143 | 144 | void PrintReport() const; 145 | 146 | protected: 147 | ShapeModelToLevelSetImageMetric(); 148 | virtual ~ShapeModelToLevelSetImageMetric() {} 149 | 150 | mutable itk::SizeValueType m_NumberOfSamplesCounted; 151 | typename PointsContainerType::Pointer m_PointsContainer; 152 | ImageConstPointer m_LevelSetImage; 153 | mutable TransformPointer m_Transform; 154 | InterpolatorPointer m_Interpolator; 155 | bool m_ComputeGradient; 156 | typename GradientImageType::Pointer m_GradientImage; 157 | typename TShapeModel::ConstPointer m_ShapeModel; 158 | double m_RegularizationParameter; 159 | unsigned int m_Degree; 160 | mutable size_t m_NumberOfEvaluations; 161 | bool m_IsInitialized; 162 | size_t m_NumberOfUsedPoints; 163 | size_t m_NumberOfParameters; 164 | 165 | // multi threading members and methods 166 | struct PerThreadData 167 | { 168 | itk::SizeValueType m_NumberOfSamplesCounted; 169 | MeasureType m_Value; 170 | DerivativeType m_Derivative; 171 | TransformJacobianType m_ModelJacobian; 172 | TransformJacobianType m_SpatialJacobian; 173 | TransformJacobianType m_Jacobian; 174 | TransformJacobianType m_JacobianCache; 175 | PointIteratorType m_Begin; 176 | PointIteratorType m_End; 177 | }; 178 | size_t m_MaximalNumberOfThreads; 179 | size_t m_NumberOfThreads; 180 | mutable std::vector m_Threads; 181 | 182 | inline void GetValueAndDerivativeThreadProcessSample(PerThreadData & data) const; 183 | void MultiThreadingInitialize(); 184 | void SparsePoints(); 185 | 186 | itk::Logger::Pointer m_Logger; 187 | mutable std::ostringstream m_Message; 188 | 189 | private: 190 | ShapeModelToLevelSetImageMetric(const Self &) ITK_DELETE_FUNCTION; 191 | void operator=(const Self &) ITK_DELETE_FUNCTION; 192 | }; 193 | } 194 | 195 | #ifndef ITK_MANUAL_INSTANTIATION 196 | #include "ssmShapeModelToLevelSetImageMetric.hxx" 197 | #endif 198 | -------------------------------------------------------------------------------- /Modules/Options/ssmOptionsBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace pt = boost::property_tree; 9 | namespace po = boost::program_options; 10 | 11 | namespace ssm 12 | { 13 | //========================================================================= 14 | // Some basic functions 15 | //========================================================================= 16 | void printTree(const pt::ptree & tree, std::ostream & os, unsigned int level = 0) 17 | { 18 | if (!tree.empty()) { 19 | os << std::endl; 20 | std::string indent(3 * level, ' '); 21 | 22 | for (const auto & it : tree) { 23 | std::string path = it.first; 24 | path.resize(16); 25 | os << indent << path << " "; 26 | 27 | printTree(it.second, os, level + 1); 28 | os << std::endl; 29 | } 30 | } 31 | 32 | std::cout << " " << tree.data(); 33 | 34 | return; 35 | } 36 | 37 | void checkParsedTree(const pt::ptree & ptreeOfRequired, pt::ptree & parsedPtree, std::string & path, std::vector & list) 38 | { 39 | if (ptreeOfRequired.empty()) { 40 | return; 41 | } 42 | 43 | for (const auto & it : ptreeOfRequired) { 44 | const auto &name = it.first; 45 | const auto &tree = it.second; 46 | 47 | if (!tree.empty()) { 48 | path = path + "." + name; 49 | checkParsedTree(ptreeOfRequired.get_child(name), parsedPtree.get_child(name), path, list); 50 | } 51 | 52 | if (parsedPtree.find(name) == parsedPtree.not_found()) { 53 | if (ptreeOfRequired.get(name)) { 54 | list.push_back(path + "." + name); 55 | } 56 | } 57 | } 58 | } 59 | 60 | std::string AddQuotes(std::string str) 61 | { 62 | return "'" + str + "'"; 63 | } 64 | 65 | //========================================================================= 66 | // Base options class 67 | //========================================================================= 68 | class OptionsBase 69 | { 70 | public: 71 | 72 | const bool & ConfigIsEnabled() const 73 | { 74 | return m_ConfigIsEnabled; 75 | } 76 | 77 | bool ParseOptions(int argc, char** argv) 78 | { 79 | // parse command line options 80 | try { 81 | po::parsed_options parsedOptions = po::command_line_parser(argc, argv).options(m_Description).run(); 82 | po::store(parsedOptions, m_Vm); 83 | po::notify(m_Vm); 84 | } 85 | catch (const po::error& e) { 86 | std::cerr << "An exception occurred while parsing the command line." << std::endl; 87 | std::cerr << e.what() << endl; 88 | std::cout << m_Description << std::endl; 89 | return false; 90 | } 91 | if (m_Help) { 92 | std::cout << m_Description << std::endl; 93 | return false; 94 | } 95 | 96 | // parse config *.ini file 97 | m_ConfigIsEnabled = !m_Vm["config"].empty(); 98 | 99 | if (m_ConfigIsEnabled) { 100 | return ParseConfigFile(); 101 | } 102 | 103 | return true; 104 | } 105 | 106 | void PrintConfig() 107 | { 108 | std::cout << std::endl; 109 | std::cout << "Config data for group " << AddQuotes(m_NameOfGroup) << std::endl; 110 | printTree(m_ParsedPtree, std::cout); 111 | std::cout << std::endl; 112 | } 113 | 114 | private: 115 | 116 | bool m_Help; 117 | bool m_ConfigIsEnabled; 118 | std::string m_Config; 119 | std::string m_NameOfGroup; 120 | char m_Dlm; 121 | 122 | pt::ptree m_ParsedPtree; 123 | pt::ptree m_PtreeOfRequired; 124 | pt::ptree m_PtreeOfDefaultValues; 125 | po::variables_map m_Vm; 126 | po::options_description m_Description; 127 | 128 | protected: 129 | OptionsBase() 130 | { 131 | m_Help = false; 132 | m_ConfigIsEnabled = false; 133 | m_Dlm = ' '; 134 | 135 | po::options_description configOptions("Optional config options"); 136 | configOptions.add_options()("config,c", po::value(&m_Config), "The path to the config file."); 137 | 138 | po::options_description helpOptions("Optional help options"); 139 | helpOptions.add_options()("help,h", po::bool_switch(&m_Help)->default_value(m_Help), "Display this help message"); 140 | 141 | m_Description.add(configOptions).add(helpOptions); 142 | } 143 | 144 | void SetNameOfGroup(const std::string & str) 145 | { 146 | m_NameOfGroup = str; 147 | } 148 | 149 | void AddToDescription(const po::options_description & options) 150 | { 151 | m_Description.add(options); 152 | } 153 | 154 | template 155 | void Put(const std::string & path, const T & value, const bool & required = true) 156 | { 157 | m_PtreeOfRequired.put(path, required); 158 | 159 | if (!required) { 160 | m_PtreeOfDefaultValues.put(path, value); 161 | } 162 | } 163 | 164 | std::string Path(const std::string & path) const 165 | { 166 | return m_NameOfGroup + "." + path; 167 | } 168 | 169 | void SetConfigFileName(const std::string & fileName) 170 | { 171 | m_Config = fileName; 172 | } 173 | 174 | bool ParseConfigFile() 175 | { 176 | try { 177 | pt::ini_parser::read_ini(m_Config, m_ParsedPtree); 178 | } 179 | catch (const pt::ptree_error &e) { 180 | std::cerr << "An exception occurred while parsing the config file: " << AddQuotes(m_Config) << std::endl; 181 | std::cerr << e.what() << std::endl; 182 | return false; 183 | } 184 | 185 | if (m_ParsedPtree.find(m_NameOfGroup) == m_ParsedPtree.not_found()) { 186 | std::cerr << "The group " << AddQuotes(m_NameOfGroup) << " is not found in the config file: " << AddQuotes(m_Config) << std::endl; 187 | return false; 188 | } 189 | 190 | m_ParsedPtree = m_ParsedPtree.get_child(m_NameOfGroup); 191 | 192 | // add default values into parsed tree 193 | pt::ptree ptree = m_PtreeOfDefaultValues; 194 | 195 | for (const auto & it : m_ParsedPtree) { 196 | ptree.put(it.first, (it.second).data()); 197 | } 198 | 199 | m_ParsedPtree = ptree; 200 | 201 | // check parsed ptree 202 | std::vector list; 203 | std::string path = m_NameOfGroup; 204 | checkParsedTree(m_PtreeOfRequired, m_ParsedPtree, path, list); 205 | 206 | // print parsed tree 207 | PrintConfig(); 208 | 209 | if (list.size() > 0) { 210 | std::cerr << "The required keys are not found in the config file: " << AddQuotes(m_Config) << std::endl; 211 | for (const auto & path : list) { 212 | std::cout << AddQuotes(path) << std::endl; 213 | } 214 | return false; 215 | } 216 | 217 | // clear map of variables from command line 218 | m_Vm.clear(); 219 | 220 | return true; 221 | } 222 | 223 | template 224 | T GetDefaultValue(const std::string & str) const 225 | { 226 | return m_PtreeOfDefaultValues.get(str); 227 | } 228 | 229 | template 230 | T Get(const std::string & path) const 231 | { 232 | T value; 233 | 234 | if (m_ConfigIsEnabled) { 235 | try { 236 | value = m_ParsedPtree.get(path); 237 | } 238 | catch (const pt::ptree_error &e) { 239 | std::cerr << "An exception occurred while getting value from ptree." << std::endl; 240 | std::cerr << e.what() << std::endl; 241 | 242 | const auto & it = m_ParsedPtree.find(path); 243 | if (it != m_ParsedPtree.not_found()) { 244 | std::cerr << AddQuotes(it->first) << " " << it->second.data() << std::endl; 245 | } 246 | 247 | throw; 248 | } 249 | } 250 | else { 251 | auto pos = path.find_last_of('.'); 252 | 253 | if (pos < path.size()) 254 | value = m_Vm[path.substr(pos + 1)].as(); 255 | else 256 | value = m_Vm[path].as(); 257 | } 258 | 259 | return value; 260 | } 261 | 262 | template 263 | std::vector GetAsVector(const std::string & path) const 264 | { 265 | std::stringstream stream(Get(path)); 266 | std::string item; 267 | 268 | std::vector vector; 269 | 270 | while (std::getline(stream, item, m_Dlm)) { 271 | // skip empty spaces '' 272 | if (item == "") 273 | continue; 274 | 275 | // get value 276 | try { 277 | vector.push_back(std::stod(item)); 278 | } 279 | catch (const std::invalid_argument &e) { 280 | std::cerr << "An exception occurred while getting vector from ptree." << std::endl; 281 | std::cerr << e.what() << " " << AddQuotes(item) << std::endl; 282 | 283 | const auto & it = m_ParsedPtree.find(path); 284 | std::cerr << AddQuotes(it->first) << " " << it->second.data() << std::endl; 285 | 286 | throw; 287 | } 288 | } 289 | 290 | return vector; 291 | } 292 | 293 | }; 294 | } 295 | -------------------------------------------------------------------------------- /Modules/Utils/ssmUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "ssmTypes.h" 18 | 19 | //! Reads a templated image from a file via ITK ImageFileReader 20 | template 21 | bool readImage(typename TImage::Pointer image, const std::string& fileName) 22 | { 23 | typedef itk::ImageFileReader Reader; 24 | typename Reader::Pointer reader = Reader::New(); 25 | reader->SetFileName(fileName); 26 | try { 27 | reader->Update(); 28 | } 29 | catch (itk::ExceptionObject& err) { 30 | std::cerr << "Unable to read image from file '" << fileName << "'" << std::endl; 31 | std::cerr << "Error: " << err << std::endl; 32 | return false; 33 | } 34 | image->Graft(reader->GetOutput()); 35 | return true; 36 | } 37 | 38 | //! print information about image 39 | template 40 | void printImageInfo(const TImage* image, const std::string &info = "") 41 | { 42 | if (info.size() > 0) { 43 | std::cout << info << std::endl; 44 | } 45 | std::cout << " size " << image->GetLargestPossibleRegion().GetSize() << ", " << image->GetNumberOfComponentsPerPixel() << std::endl; 46 | std::cout << " origin " << image->GetOrigin() << std::endl; 47 | std::cout << "spacing " << image->GetSpacing() << std::endl; 48 | std::cout << "direction" << std::endl << image->GetDirection() << std::endl; 49 | } 50 | 51 | //! print information about mesh 52 | template 53 | void printMeshInfo(const TMesh* surface, const std::string &info = "") 54 | { 55 | if (info.size() > 0) { 56 | std::cout << info << std::endl; 57 | } 58 | std::cout << "number of cells " << surface->GetNumberOfCells() << std::endl; 59 | std::cout << "number of points " << surface->GetNumberOfPoints() << std::endl; 60 | std::cout << std::endl; 61 | } 62 | 63 | //! Writes a templated image to a file via ITK ImageFileWriter 64 | template 65 | bool writeImage(const TImage* image, const std::string& fileName) 66 | { 67 | typedef itk::ImageFileWriter Writer; 68 | typename Writer::Pointer writer = Writer::New(); 69 | writer->SetInput(image); 70 | writer->SetFileName(fileName); 71 | writer->SetUseCompression(true); 72 | try { 73 | writer->Update(); 74 | } 75 | catch (itk::ExceptionObject& err) { 76 | std::cerr << "Unable to write image to file '" << fileName << "'" << std::endl; 77 | std::cerr << "Error: " << err << std::endl; 78 | return false; 79 | } 80 | return true; 81 | } 82 | 83 | //! Reads a mesh from a file 84 | template 85 | bool readMesh(typename TMesh::Pointer mesh, const std::string& fileName) 86 | { 87 | typedef itk::MeshFileReader MeshFileReader; 88 | typename MeshFileReader::Pointer reader = MeshFileReader::New(); 89 | reader->SetFileName(fileName); 90 | try { 91 | reader->Update(); 92 | } 93 | catch (itk::ExceptionObject& err) { 94 | std::cerr << "Unable to read mesh to file '" << fileName << "'" << std::endl; 95 | std::cerr << "Error: " << err << std::endl; 96 | return false; 97 | } 98 | mesh->Graft(reader->GetOutput()); 99 | return true; 100 | } 101 | 102 | //! Writes a mesh to a file 103 | template 104 | bool writeMesh(const TMesh* mesh, const std::string& fileName) 105 | { 106 | typedef itk::MeshFileWriter MeshFileWriter; 107 | typename MeshFileWriter::Pointer writer = MeshFileWriter::New(); 108 | writer->SetFileName(fileName); 109 | writer->SetInput(mesh); 110 | writer->SetUseCompression(true); 111 | writer->SetFileTypeAsBINARY(); 112 | try { 113 | writer->Update(); 114 | } 115 | catch (itk::ExceptionObject& err) { 116 | std::cerr << "Unable to write mesh to file '" << fileName << "'" << std::endl; 117 | std::cerr << "Error: " << err << std::endl; 118 | return false; 119 | } 120 | return true; 121 | } 122 | 123 | bool readVTKPolydata(vtkPolyData* surface, const std::string& filename) 124 | { 125 | typedef vtkSmartPointer Reader; 126 | Reader reader = Reader::New(); 127 | reader->SetFileName(filename.c_str()); 128 | reader->Update(); 129 | surface->ShallowCopy(reader->GetOutput()); 130 | return true; 131 | } 132 | 133 | bool writeVTKPolydata(vtkPolyData* surface, const std::string& fileName) 134 | { 135 | typedef vtkSmartPointer Writer; 136 | 137 | Writer writer = Writer::New(); 138 | writer->SetInputData(surface); 139 | writer->SetFileName(fileName.c_str()); 140 | writer->SetFileTypeToBinary(); 141 | bool result = static_cast(writer->Write()); 142 | if (!result) { 143 | std::cerr << "Error: Unable to write surface to file '" << fileName << "'" << std::endl; 144 | } 145 | return result; 146 | } 147 | 148 | //! Writes a transform to a file 149 | template 150 | bool writeTransform(const TransformType* transform, const std::string& fileName) 151 | { 152 | typedef typename TransformType::ScalarType ScalarType; 153 | 154 | typename itk::TransformFileWriterTemplate::Pointer writer = itk::TransformFileWriterTemplate::New(); 155 | writer->SetInput(transform); 156 | writer->SetFileName(fileName); 157 | try { 158 | writer->Update(); 159 | } 160 | catch (itk::ExceptionObject& err) { 161 | std::cerr << "Unable to write transform to file '" << fileName << "'" << std::endl; 162 | std::cerr << "Error: " << err << std::endl; 163 | return false; 164 | } 165 | return true; 166 | } 167 | 168 | //! Reads a transform from a file 169 | typedef itk::TransformFileReader::TransformListType * TransformListType; 170 | bool readTransform(TransformListType transforms, const std::string& fileName) 171 | { 172 | itk::TransformFactoryBase::RegisterDefaultTransforms(); 173 | itk::TransformFileReader::Pointer reader = itk::TransformFileReader::New(); 174 | reader->SetFileName(fileName); 175 | try { 176 | reader->Update(); 177 | } 178 | catch (itk::ExceptionObject& err) { 179 | std::cerr << "Unable to read transform from file '" << fileName << "'" << std::endl; 180 | std::cerr << "Error: " << err << std::endl; 181 | return false; 182 | } 183 | transforms = reader->GetTransformList(); 184 | return true; 185 | } 186 | 187 | std::string getDirectoryFromPath(const std::string& fileName) 188 | { 189 | boost::filesystem::path path(fileName); 190 | return path.parent_path().string(); 191 | } 192 | 193 | std::string getFileNameFromPath(const std::string& fileName) 194 | { 195 | boost::filesystem::path path(fileName); 196 | return path.filename().string(); 197 | } 198 | 199 | std::string addFileNameSuffix(const std::string& fileName, const::std::string& suffix) 200 | { 201 | boost::filesystem::path path(fileName); 202 | path = path.parent_path() / boost::filesystem::path(path.stem().string() + suffix + path.extension().string()); 203 | return path.string(); 204 | } 205 | 206 | std::string getBaseNameFromPath(const std::string& fileName) 207 | { 208 | boost::filesystem::path path(fileName); 209 | return path.stem().string(); 210 | } 211 | 212 | StringVector readListFromFile(const std::string& fileName) 213 | { 214 | StringVector list; 215 | 216 | std::ifstream file; 217 | try { 218 | file.exceptions(std::ifstream::failbit | std::ifstream::badbit); 219 | file.open(fileName.c_str(), std::ifstream::in); 220 | std::string line; 221 | while (getline(file, line)) { 222 | if (line != "") { 223 | //reading files with windows EOL on Linux results in the \r not being removed from the line ending 224 | if (*line.rbegin() == '\r') { 225 | line.erase(line.length() - 1, 1); 226 | } 227 | list.push_back(line); 228 | } 229 | } 230 | } 231 | catch (std::ifstream::failure e) { 232 | if (file.eof() == false) { 233 | throw std::ifstream::failure("Failed to read list from the file: " + fileName); 234 | } 235 | } 236 | 237 | return list; 238 | } 239 | 240 | void writeListToFile(const std::string & fileName, const StringVector & list) 241 | { 242 | std::ofstream file(fileName, std::ofstream::out); 243 | if (!file.is_open()) { 244 | throw std::ofstream::failure("Failed to write list to the file : " + fileName); 245 | } 246 | 247 | for (const auto & string : list) { 248 | file << string << std::endl; 249 | } 250 | file.close(); 251 | 252 | return; 253 | } 254 | 255 | bool checkFileName(const std::string & fileName) 256 | { 257 | std::ofstream file(fileName, std::ofstream::out); 258 | if (!file.is_open()) { 259 | std::cerr << "Failed to open file " << fileName << std::endl; 260 | return false; 261 | } 262 | 263 | file.close(); 264 | boost::filesystem::remove(fileName); 265 | 266 | return true; 267 | } 268 | -------------------------------------------------------------------------------- /Modules/SSM/ssmPointSetToImageMetrics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace ssm 12 | { 13 | template< typename TPointSet, typename TImage > 14 | class PointSetToImageMetrics : public itk::Object 15 | { 16 | public: 17 | 18 | /** Standard class typedefs. */ 19 | typedef PointSetToImageMetrics Self; 20 | typedef itk::Object Superclass; 21 | typedef itk::SmartPointer< Self > Pointer; 22 | typedef itk::SmartPointer< const Self > ConstPointer; 23 | 24 | /** Method for creation through the object factory. */ 25 | itkNewMacro(Self); 26 | 27 | /** Run-time type information (and related methods). */ 28 | itkTypeMacro(PointSetToImageMetrics, Object); 29 | 30 | /** Constants for the image dimensions */ 31 | itkStaticConstMacro(Dimension, unsigned int, TPointSet::PointDimension); 32 | static_assert(TPointSet::PointDimension == TImage::ImageDimension, "Invalid dimensions of the input data."); 33 | 34 | /** Types transferred from the base class */ 35 | typedef TPointSet PointSetType; 36 | typedef typename PointSetType::PointType PointType; 37 | typedef TImage ImageType; 38 | typedef typename PointSetType::PointsContainerConstIterator PointIterator; 39 | typedef itk::LinearInterpolateImageFunction InterpolatorType; 40 | 41 | /** Type of the additional information. */ 42 | typedef std::pair PairType; 43 | typedef std::vector InfoType; 44 | 45 | void SetInfo(const InfoType& info) { m_Info = info; } 46 | 47 | /** Set the point set as vtkPolyData. */ 48 | template< typename PolyData> 49 | void SetPointSetAsPolyData(PolyData * surface) 50 | { 51 | if (Dimension != 3) { 52 | itkExceptionMacro(<<"For the method SetSurfaceAsPolyData dimension 3 is supproted.") 53 | } 54 | 55 | auto points = PointSetType::New(); 56 | PointType point; 57 | 58 | for (size_t n = 0; n < surface->GetPoints()->GetNumberOfPoints(); ++n) { 59 | for (size_t i = 0; i < Dimension; ++i) { 60 | point[i] = surface->GetPoints()->GetPoint(n)[i]; 61 | } 62 | points->SetPoint(n, point); 63 | } 64 | 65 | this->SetPointSet(points); 66 | } 67 | 68 | template< typename MeshType> 69 | void SetPointSetAsMesh(const MeshType * mesh) 70 | { 71 | auto points = PointSetType::New(); 72 | points->SetPoints(const_cast (mesh->GetPoints())); 73 | this->SetPointSet(points); 74 | } 75 | 76 | /** Get/Set the fixed point set. */ 77 | itkSetConstObjectMacro(PointSet, PointSetType); 78 | itkGetConstObjectMacro(PointSet, PointSetType); 79 | 80 | /** Get/Set the moving image. */ 81 | itkSetConstObjectMacro(Image, ImageType); 82 | itkGetConstObjectMacro(Image, ImageType); 83 | 84 | /*Get/Set values to compute quantile. */ 85 | itkSetMacro(LevelOfQuantile, double); 86 | itkGetMacro(LevelOfQuantile, double); 87 | 88 | itkSetMacro(HistogramSize, size_t); 89 | itkGetMacro(HistogramSize, size_t); 90 | 91 | /*Get metrics values. */ 92 | itkGetMacro(MeanValue, double); 93 | itkGetMacro(RMSEValue, double); 94 | itkGetMacro(QuantileValue, double); 95 | itkGetMacro(MaximalValue, double); 96 | 97 | void PrintReport() const 98 | { 99 | if (!m_Info.empty()) { 100 | std::cout << "Information" << std::endl; 101 | for (const auto & pair : m_Info) { 102 | std::cout << pair.first << " " << pair.second << std::endl; 103 | } 104 | std::cout << std::endl; 105 | } 106 | 107 | std::cout << "Metric values" << std::endl; 108 | std::cout << "Mean " << m_MeanValue << std::endl; 109 | std::cout << "RMSE " << m_RMSEValue << std::endl; 110 | std::cout << "Quantile " << m_QuantileValue << ", level = " << m_LevelOfQuantile << std::endl; 111 | std::cout << "Maximal " << m_MaximalValue << std::endl; 112 | std::cout << std::endl; 113 | } 114 | 115 | void PrintReportToFile(const std::string & fileName, const std::string & datasetURI) const 116 | { 117 | // print report to *.csv file 118 | if (!fileName.size()) { 119 | return; 120 | } 121 | 122 | std::cout << "print report to the file " << fileName << std::endl; 123 | std::cout << std::endl; 124 | 125 | std::string dlm = ";"; 126 | 127 | std::string header = dlm; 128 | std::string scores = datasetURI + dlm; 129 | 130 | header += "Mean" + dlm; 131 | scores += std::to_string(m_MeanValue) + dlm; 132 | 133 | header += "RMSE" + dlm; 134 | scores += std::to_string(m_RMSEValue) + dlm; 135 | 136 | header += "Quantile " + std::to_string(m_LevelOfQuantile) + dlm; 137 | scores += std::to_string(m_QuantileValue) + dlm; 138 | 139 | header += "Maximal" + dlm; 140 | scores += std::to_string(m_MaximalValue) + dlm; 141 | 142 | header += dlm; 143 | scores += dlm; 144 | 145 | for (auto it = m_Info.begin(); it != m_Info.end(); ++it) { 146 | header += (*it).first + dlm; 147 | scores += (*it).second + dlm; 148 | } 149 | 150 | bool exist = boost::filesystem::exists(fileName); 151 | std::ofstream file(fileName, std::ofstream::out | std::ofstream::app); 152 | if (!file.is_open()) { 153 | throw std::ofstream::failure("Failed to write report to the file " + fileName); 154 | } 155 | 156 | if (!exist) { 157 | file << header << std::endl; 158 | } 159 | 160 | file << scores << std::endl; 161 | file.close(); 162 | } 163 | 164 | /** Compute values. */ 165 | void Compute() 166 | { 167 | if (!m_PointSet) { 168 | itkExceptionMacro(<< "Fixed point set has not been assigned"); 169 | } 170 | 171 | if (!m_Image) { 172 | itkExceptionMacro(<< "Moving image has not been assigned"); 173 | } 174 | if (m_Image->GetSource()) { 175 | m_Image->GetSource()->Update(); 176 | } 177 | 178 | m_Interpolator = InterpolatorType::New(); 179 | m_Interpolator->SetInputImage(m_Image); 180 | 181 | m_MeanValue = 0; 182 | m_RMSEValue = 0; 183 | m_MaximalValue = 0; 184 | 185 | typedef itk::Vector VectorType; 186 | typedef itk::Statistics::ListSample ListSampleType; 187 | ListSampleType::Pointer sample = ListSampleType::New(); 188 | 189 | m_NumberOfPixelsCounted = 0; 190 | 191 | for (PointIterator it = m_PointSet->GetPoints()->Begin(); it != m_PointSet->GetPoints()->End(); ++it) { 192 | PointType point = it.Value(); 193 | 194 | if (m_Interpolator->IsInsideBuffer(point)) { 195 | const double value = std::abs(m_Interpolator->Evaluate(point)); 196 | sample->PushBack(value); 197 | 198 | m_MeanValue += value; 199 | m_RMSEValue += value * value; 200 | m_MaximalValue = std::max(value, m_MaximalValue); 201 | 202 | m_NumberOfPixelsCounted++; 203 | } 204 | } 205 | 206 | if (!m_NumberOfPixelsCounted) { 207 | itkExceptionMacro(<< "All the points mapped to outside of the moving image"); 208 | } 209 | 210 | typedef typename itk::Statistics::Histogram HistogramType; 211 | typename HistogramType::SizeType size(1); 212 | size.Fill(m_HistogramSize); 213 | 214 | typedef itk::Statistics::SampleToHistogramFilter SampleToHistogramFilterType; 215 | SampleToHistogramFilterType::Pointer sampleToHistogram = SampleToHistogramFilterType::New(); 216 | sampleToHistogram->SetInput(sample); 217 | sampleToHistogram->SetAutoMinimumMaximum(true); 218 | sampleToHistogram->SetHistogramSize(size); 219 | try { 220 | sampleToHistogram->Update(); 221 | } 222 | catch (itk::ExceptionObject& excep) { 223 | itkExceptionMacro(<< excep); 224 | } 225 | 226 | m_MeanValue = m_MeanValue / m_NumberOfPixelsCounted; 227 | m_RMSEValue = std::sqrt(m_RMSEValue / m_NumberOfPixelsCounted); 228 | m_QuantileValue = sampleToHistogram->GetOutput()->Quantile(0, m_LevelOfQuantile); 229 | } 230 | 231 | protected: 232 | PointSetToImageMetrics() {} 233 | virtual ~PointSetToImageMetrics() {} 234 | 235 | private: 236 | PointSetToImageMetrics(const Self &); // purposely not implemented 237 | void operator=(const Self &); // purposely not implemented 238 | 239 | typename InterpolatorType::Pointer m_Interpolator; 240 | 241 | double m_LevelOfQuantile = 0.95; 242 | size_t m_HistogramSize = 1000; 243 | size_t m_NumberOfPixelsCounted; 244 | 245 | double m_MeanValue; 246 | double m_RMSEValue; 247 | double m_QuantileValue; 248 | double m_MaximalValue; 249 | InfoType m_Info; 250 | 251 | typename PointSetType::ConstPointer m_PointSet; 252 | typename ImageType::ConstPointer m_Image; 253 | }; 254 | } 255 | -------------------------------------------------------------------------------- /Modules/SSM/ssmMeshToImageRegistrationMethod.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "ssmMeshToImageRegistrationMethod.h" 18 | 19 | namespace ssm 20 | { 21 | //---------------------------------------------------------------------------- 22 | template 23 | MeshToImageRegistrationMethod::MeshToImageRegistrationMethod() 24 | { 25 | this->SetNumberOfRequiredInputs(1); 26 | this->SetNumberOfRequiredOutputs(1); 27 | this->SetNthOutput(0, TOutputMesh::New()); 28 | } 29 | //---------------------------------------------------------------------------- 30 | template 31 | void MeshToImageRegistrationMethod::GenerateData() 32 | { 33 | m_Surface = const_cast (this->GetInput()); 34 | 35 | //define point set 36 | m_PointSet = PointSetType::New(); 37 | m_PointSet->SetPoints(m_Surface->GetPoints()); 38 | 39 | typename PointSetType::PointDataContainer::Pointer pointData = PointSetType::PointDataContainer::New(); 40 | pointData->Reserve(m_PointSet->GetNumberOfPoints()); 41 | 42 | for (auto it = pointData->Begin(); it != pointData->End(); ++it) { 43 | it->Value() = 0; 44 | } 45 | m_PointSet->SetPointData(pointData); 46 | 47 | this->InitializeTransform(); 48 | 49 | // setup interpolator 50 | m_Interpolator = InterpolatorType::New(); 51 | 52 | // setup metric 53 | typedef itk::MeanSquaresPointSetToImageMetric MetricType; 54 | m_Metric = MetricType::New(); 55 | m_Metric->SetMovingImage(m_LevelsetImage); 56 | m_Metric->SetFixedPointSet(m_PointSet); 57 | m_Metric->SetTransform(m_Transform); 58 | m_Metric->SetInterpolator(m_Interpolator); 59 | try { 60 | m_Metric->Initialize(); 61 | } 62 | catch (itk::ExceptionObject& excep) { 63 | std::cout << excep << std::endl; 64 | itkExceptionMacro(<< excep); 65 | } 66 | 67 | // setup optimizer 68 | m_Optimizer = OptimizerType::New(); 69 | m_Optimizer->SetCostFunction(m_Metric); 70 | m_Optimizer->SetInitialPosition(m_Transform->GetParameters()); 71 | m_Optimizer->SetMaximumNumberOfFunctionEvaluations(m_NumberOfIterations); 72 | m_Optimizer->SetScales(m_Scales); 73 | m_Optimizer->SetGradientConvergenceTolerance(m_GradientConvergenceTolerance); 74 | m_Optimizer->SetLineSearchAccuracy(m_LineSearchAccuracy); 75 | m_Optimizer->SetDefaultStepLength(m_DefaultStepLength); 76 | m_Optimizer->MinimizeOn(); 77 | try { 78 | m_Optimizer->StartOptimization(); 79 | } 80 | catch (itk::ExceptionObject& excep) { 81 | std::cout << excep << std::endl; 82 | itkExceptionMacro(<< excep); 83 | } 84 | 85 | this->GenerateOutputData(); 86 | } 87 | //---------------------------------------------------------------------------- 88 | template 89 | void MeshToImageRegistrationMethod::ComputeLabelImage() 90 | { 91 | typedef itk::BinaryThresholdImageFilter BinaryThresholdImageFilterType; 92 | typename BinaryThresholdImageFilterType::Pointer threshold = BinaryThresholdImageFilterType::New(); 93 | threshold->SetInput(m_LevelsetImage); 94 | threshold->SetLowerThreshold(double(std::numeric_limits::lowest())); 95 | threshold->SetUpperThreshold(0); 96 | threshold->SetInsideValue(1); 97 | threshold->SetOutsideValue(0); 98 | threshold->Update(); 99 | 100 | m_Mask = threshold->GetOutput(); 101 | } 102 | 103 | //---------------------------------------------------------------------------- 104 | template 105 | void MeshToImageRegistrationMethod::InitializeTransform() 106 | { 107 | // compute label of the input level set image to initialize transform 108 | this->ComputeLabelImage(); 109 | 110 | // Compute a bounding box of the input mesh 111 | typename TOutputMesh::BoundingBoxType::ConstPointer boundingBox = m_Surface->GetBoundingBox(); 112 | 113 | typename BinaryImageType::SpacingType spacing = m_Mask->GetSpacing(); 114 | typename BinaryImageType::PointType origin = boundingBox->GetMinimum(); 115 | typename BinaryImageType::SizeType size; 116 | 117 | for (int n = 0; n < PointDimension; ++n) { 118 | size[n] = (boundingBox->GetMaximum()[n] - boundingBox->GetMinimum()[n]) / spacing[n]; 119 | } 120 | 121 | typedef itk::TriangleMeshToBinaryImageFilter ShapeToBinaryImageFilterType; 122 | typename ShapeToBinaryImageFilterType::Pointer shapeToImage = ShapeToBinaryImageFilterType::New(); 123 | shapeToImage->SetInput(m_Surface); 124 | shapeToImage->SetSize(size); 125 | shapeToImage->SetOrigin(origin); 126 | shapeToImage->SetSpacing(spacing); 127 | shapeToImage->SetDirection(m_Mask->GetDirection()); 128 | shapeToImage->SetOutsideValue(0); 129 | shapeToImage->SetInsideValue(1); 130 | try { 131 | shapeToImage->Update(); 132 | } 133 | catch (itk::ExceptionObject& excep) { 134 | std::cout << excep << std::endl; 135 | itkExceptionMacro(<< excep); 136 | } 137 | 138 | // moment calculators 139 | typedef itk::ImageMomentsCalculator ImageCalculatorType; 140 | typedef typename ImageCalculatorType::VectorType VectorType; 141 | 142 | typename ImageCalculatorType::Pointer movingCalculator = ImageCalculatorType::New(); 143 | movingCalculator->SetImage(shapeToImage->GetOutput()); 144 | movingCalculator->Compute(); 145 | 146 | typename ImageCalculatorType::Pointer fixedCalculator = ImageCalculatorType::New(); 147 | fixedCalculator->SetImage(m_Mask); 148 | fixedCalculator->Compute(); 149 | 150 | typename TransformType::InputPointType center = movingCalculator->GetCenterOfGravity(); 151 | typename TransformType::OutputVectorType translation = fixedCalculator->GetCenterOfGravity() - movingCalculator->GetCenterOfGravity(); 152 | 153 | // initialize spatial transform 154 | InitializeTransformType::Pointer initializer = InitializeTransformType::New(); 155 | initializer->SetTransformType(m_TypeOfTransform); 156 | initializer->SetCenter(center); 157 | initializer->SetTranslation(translation); 158 | try { 159 | initializer->Initialize(); 160 | } 161 | catch (itk::ExceptionObject& excep) { 162 | std::cout << excep << std::endl; 163 | itkExceptionMacro(<< excep); 164 | } 165 | m_Transform = initializer->GetTransform(); 166 | 167 | m_Scales = initializer->GetScales(); 168 | for (size_t i = 0; i < m_Scales.size(); ++i) { 169 | m_Scales[i] = 1 / m_Scales[i]; 170 | } 171 | } 172 | //---------------------------------------------------------------------------- 173 | template 174 | void MeshToImageRegistrationMethod::GenerateOutputData() 175 | { 176 | //compute moved output 177 | typedef itk::TransformMeshFilter TransformFilterType; 178 | typename TransformFilterType::Pointer transform = TransformFilterType::New(); 179 | transform->SetInput(m_Surface); 180 | transform->SetTransform(m_Transform); 181 | try { 182 | transform->Update(); 183 | } 184 | catch (itk::ExceptionObject& excep) { 185 | std::cout << excep << std::endl; 186 | itkExceptionMacro(<< excep); 187 | } 188 | 189 | this->GraftNthOutput(0, transform->GetOutput()); 190 | } 191 | //---------------------------------------------------------------------------- 192 | template 193 | void MeshToImageRegistrationMethod::PrintReport(std::ostream& os) const 194 | { 195 | os << this->GetNameOfClass() << std::endl; 196 | os << std::endl; 197 | 198 | if (m_NumberOfIterations > 0) { 199 | os << " stop condition description " << m_Optimizer->GetStopConditionDescription() << std::endl; 200 | os << " line search accuracy " << m_Optimizer->GetLineSearchAccuracy() << std::endl; 201 | os << " gradient convergence tolerance " << m_Optimizer->GetGradientConvergenceTolerance() << std::endl; 202 | os << " default step length " << m_Optimizer->GetDefaultStepLength() << std::endl; 203 | os << " number of function evaluations " << m_Optimizer->GetMaximumNumberOfFunctionEvaluations() << std::endl; 204 | os << " cost function value " << m_Optimizer->GetValue() << std::endl; 205 | os << std::endl; 206 | } 207 | 208 | os << "transform" << std::endl; 209 | os << m_Transform->GetTransformTypeAsString() << ", category " << m_Transform->GetTransformCategory() << std::endl; 210 | os << "scales " << m_Scales << std::endl; 211 | os << "parameters " << m_Transform->GetParameters() << ", " << m_Transform->GetNumberOfParameters() << std::endl; 212 | os << std::endl; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /Modules/SSM/ssmPointSetToPointSetMetrics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace ssm 12 | { 13 | template< typename TFixedPointSet, typename TMovingPointSet = TFixedPointSet > 14 | class PointSetToPointSetMetrics : public itk::Object 15 | { 16 | public: 17 | /** Standard class typedefs. */ 18 | typedef PointSetToPointSetMetrics Self; 19 | typedef itk::Object Superclass; 20 | typedef itk::SmartPointer< Self > Pointer; 21 | typedef itk::SmartPointer< const Self > ConstPointer; 22 | 23 | /** Method for creation through the object factory. */ 24 | itkNewMacro(Self); 25 | 26 | /** Run-time type information (and related methods). */ 27 | itkTypeMacro(PointSetToPointSetMetrics, itk::Object); 28 | 29 | /** Types transferred from the base class */ 30 | typedef double MeasureType; 31 | typedef TFixedPointSet FixedPointSetType; 32 | typedef TMovingPointSet MovingPointSetType; 33 | typedef typename FixedPointSetType::ConstPointer FixedPointSetConstPointer; 34 | typedef typename MovingPointSetType::ConstPointer MovingPointSetConstPointer; 35 | 36 | /** Type of the parameters. */ 37 | typedef itk::Statistics::ListSample ListSampleType; 38 | typedef itk::Statistics::KdTreeGenerator TreeGeneratorType; 39 | typedef typename TreeGeneratorType::KdTreeType TreeType; 40 | typedef typename TreeType::InstanceIdentifierVectorType NeighborhoodType; 41 | 42 | /** Type of the additional information. */ 43 | typedef std::pair PairType; 44 | typedef std::vector InfoType; 45 | 46 | void SetInfo(InfoType& info) 47 | { 48 | m_Info = info; 49 | } 50 | 51 | /** Get/Set the Fixed Point Set. */ 52 | itkSetConstObjectMacro(FixedPointSet, FixedPointSetType); 53 | itkGetConstObjectMacro(FixedPointSet, FixedPointSetType); 54 | 55 | /** Get/Set the Moving Image. */ 56 | itkSetConstObjectMacro(MovingPointSet, MovingPointSetType); 57 | itkGetConstObjectMacro(MovingPointSet, MovingPointSetType); 58 | 59 | /* Compute symmetric metrics*/ 60 | itkSetMacro(Symmetric, bool); 61 | itkGetMacro(Symmetric, bool); 62 | 63 | /*Get/Set values to compute quantile. */ 64 | itkSetMacro(LevelOfQuantile, double); 65 | itkGetMacro(LevelOfQuantile, double); 66 | 67 | itkSetMacro(HistogramSize, size_t); 68 | itkGetMacro(HistogramSize, size_t); 69 | 70 | /*Get metrics values. */ 71 | itkGetMacro(MeanValue, MeasureType); 72 | itkGetMacro(RMSEValue, MeasureType); 73 | itkGetMacro(QuantileValue, MeasureType); 74 | itkGetMacro(MaximalValue, MeasureType); 75 | 76 | void PrintReport(std::ostream& os) const 77 | { 78 | std::string indent = " "; 79 | os << "Metric values:" << std::endl; 80 | os << indent << " Mean = " << m_MeanValue << std::endl; 81 | os << indent << " RMSE = " << m_RMSEValue << std::endl; 82 | os << indent << "Quantile = " << m_QuantileValue << ", level = " << m_LevelOfQuantile << std::endl; 83 | os << indent << " Maximal = " << m_MaximalValue << std::endl; 84 | os << std::endl; 85 | } 86 | 87 | void PrintReportToFile(const std::string & fileName, const std::string & datasetURI) const 88 | { 89 | std::string dlm = ";"; 90 | 91 | std::string header = dlm; 92 | std::string scores = datasetURI + dlm; 93 | 94 | header += "Mean" + dlm; 95 | scores += std::to_string(m_MeanValue) + dlm; 96 | 97 | header += "RMSE" + dlm; 98 | scores += std::to_string(m_RMSEValue) + dlm; 99 | 100 | header += "Quantile " + std::to_string(m_LevelOfQuantile) + dlm; 101 | scores += std::to_string(m_QuantileValue) + dlm; 102 | 103 | header += "Maximal" + dlm; 104 | scores += std::to_string(m_MaximalValue) + dlm; 105 | 106 | header += dlm; 107 | scores += dlm; 108 | 109 | for (auto it = m_Info.begin(); it != m_Info.end(); ++it) { 110 | header += (*it).first + dlm; 111 | scores += (*it).second + dlm; 112 | } 113 | 114 | bool exist = boost::filesystem::exists(fileName); 115 | std::ofstream file(fileName, std::ofstream::out | std::ofstream::app); 116 | 117 | if (!exist) { 118 | file << header << std::endl; 119 | } 120 | 121 | file << scores << std::endl; 122 | file.close(); 123 | } 124 | 125 | /** Compute metrics. */ 126 | void Compute() 127 | { 128 | std::vector fixedToMovingMetrics; 129 | this->ComputeMetrics(fixedToMovingMetrics, m_FixedPointSet, m_MovingPointSet); 130 | 131 | m_MeanValue = fixedToMovingMetrics[0]; 132 | m_RMSEValue = fixedToMovingMetrics[1]; 133 | m_QuantileValue = fixedToMovingMetrics[2]; 134 | m_MaximalValue = fixedToMovingMetrics[3]; 135 | 136 | if (m_Symmetric) { 137 | std::vector movingToFixedMetrics; 138 | this->ComputeMetrics(movingToFixedMetrics, m_MovingPointSet, m_FixedPointSet); 139 | 140 | m_MeanValue = (fixedToMovingMetrics[0] + movingToFixedMetrics[0])/2; 141 | m_RMSEValue = (fixedToMovingMetrics[1] + movingToFixedMetrics[1])/2; 142 | m_QuantileValue = (fixedToMovingMetrics[2] + movingToFixedMetrics[2])/2; 143 | m_MaximalValue = (fixedToMovingMetrics[3] + movingToFixedMetrics[3])/2; 144 | } 145 | } 146 | 147 | 148 | protected: 149 | PointSetToPointSetMetrics() {} 150 | virtual ~PointSetToPointSetMetrics() {} 151 | 152 | private: 153 | PointSetToPointSetMetrics(const Self &); 154 | void operator=(const Self &); 155 | 156 | FixedPointSetConstPointer m_FixedPointSet; 157 | MovingPointSetConstPointer m_MovingPointSet; 158 | 159 | size_t m_BucketSize = 16; 160 | size_t m_HistogramSize = 1000; 161 | double m_LevelOfQuantile = 0.95; 162 | 163 | bool m_Symmetric = true; 164 | MeasureType m_MeanValue; 165 | MeasureType m_RMSEValue; 166 | MeasureType m_QuantileValue; 167 | MeasureType m_MaximalValue; 168 | InfoType m_Info; 169 | 170 | typename ListSampleType::Pointer m_ListOfPoints; 171 | typename TreeType::ConstPointer m_Tree; 172 | 173 | void ComputeMetrics(std::vector & metrics, typename FixedPointSetType::ConstPointer fixedPointSet, typename MovingPointSetType::ConstPointer movingPointSet) 174 | { 175 | this->InitializeKdTree(movingPointSet); 176 | 177 | MeasureType mean = itk::NumericTraits::Zero; 178 | MeasureType rmse = itk::NumericTraits::Zero; 179 | MeasureType maximal = itk::NumericTraits::Zero; 180 | 181 | typename FixedPointSetType::PointsContainer::ConstPointer fixedContainer = fixedPointSet->GetPoints(); 182 | typename MovingPointSetType::PointsContainer::ConstPointer movingContainer = movingPointSet->GetPoints(); 183 | 184 | typedef itk::Vector VectorType; 185 | typedef itk::Statistics::ListSample ListSampleType; 186 | ListSampleType::Pointer measures = ListSampleType::New(); 187 | 188 | for (typename FixedPointSetType::PointsContainerConstIterator fixed = fixedContainer->Begin(); fixed != fixedContainer->End(); ++fixed) { 189 | typename FixedPointSetType::PointType fixedPoint = fixed.Value(); 190 | 191 | NeighborhoodType neighbors; 192 | std::vector distancies; 193 | m_Tree->Search(fixedPoint, 1, neighbors, distancies); 194 | if (distancies.size() == 0) { 195 | continue; 196 | } 197 | MeasureType distance = distancies[0]; 198 | 199 | measures->PushBack(distance); 200 | mean += distance; 201 | rmse += distance * distance; 202 | maximal = std::max(maximal, distance); 203 | } 204 | 205 | mean = mean / fixedPointSet->GetNumberOfPoints(); 206 | rmse = std::sqrt(rmse / fixedPointSet->GetNumberOfPoints()); 207 | 208 | // compute quantile 209 | typedef typename itk::Statistics::Histogram HistogramType; 210 | typename HistogramType::SizeType size(1); 211 | size.Fill(m_HistogramSize); 212 | 213 | typedef itk::Statistics::SampleToHistogramFilter SampleToHistogramFilterType; 214 | SampleToHistogramFilterType::Pointer sampleToHistogram = SampleToHistogramFilterType::New(); 215 | sampleToHistogram->SetInput(measures); 216 | sampleToHistogram->SetAutoMinimumMaximum(true); 217 | sampleToHistogram->SetHistogramSize(size); 218 | try { 219 | sampleToHistogram->Update(); 220 | } 221 | catch (itk::ExceptionObject& excep) { 222 | itkExceptionMacro(<< excep); 223 | } 224 | MeasureType quantile = sampleToHistogram->GetOutput()->Quantile(0, m_LevelOfQuantile); 225 | 226 | metrics.resize(0); 227 | metrics.push_back(mean); 228 | metrics.push_back(rmse); 229 | metrics.push_back(quantile); 230 | metrics.push_back(maximal); 231 | } 232 | 233 | void InitializeKdTree(typename MovingPointSetType::ConstPointer pointSet) 234 | { 235 | m_ListOfPoints = ListSampleType::New(); 236 | for (typename MovingPointSetType::PointsContainerConstIterator it = pointSet->GetPoints()->Begin(); it != pointSet->GetPoints()->End(); ++it) { 237 | m_ListOfPoints->PushBack(it.Value()); 238 | } 239 | 240 | typename TreeGeneratorType::Pointer generator = TreeGeneratorType::New(); 241 | generator->SetSample(m_ListOfPoints); 242 | generator->SetBucketSize(m_BucketSize); 243 | try { 244 | generator->Update(); 245 | } 246 | catch (itk::ExceptionObject& excep) { 247 | itkExceptionMacro(<< excep); 248 | } 249 | 250 | m_Tree = generator->GetOutput(); 251 | } 252 | }; 253 | } 254 | -------------------------------------------------------------------------------- /Modules/SSM/ssmShapeModelRegistrationMethodBase.hxx: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "ssmShapeModelRegistrationMethodBase.h" 6 | 7 | namespace ssm 8 | { 9 | //---------------------------------------------------------------------------- 10 | template 11 | ShapeModelRegistrationMethodBase::ShapeModelRegistrationMethodBase() 12 | { 13 | this->SetNumberOfRequiredInputs(0); 14 | this->SetNumberOfRequiredOutputs(1); 15 | this->SetNthOutput(0, TOutputMesh::New()); 16 | 17 | m_ShapeModel = nullptr; 18 | 19 | m_SpatialTransform = nullptr; 20 | m_Transform = nullptr; 21 | 22 | m_ModelScale = 3; 23 | m_NumberOfIterations = 500; 24 | 25 | m_UseNumberOfEpochs = true; 26 | m_NumberOfEpochs = 1; 27 | m_Step = 0; 28 | 29 | m_Optimizer = OptimizerType::New(); 30 | m_Optimizer->SetMaximumNumberOfFunctionEvaluations(m_NumberOfIterations); 31 | m_Optimizer->SetLineSearchAccuracy(0.1); 32 | m_Optimizer->SetDefaultStepLength(0.5); 33 | m_Optimizer->SetGradientConvergenceTolerance(1.0e-05); 34 | m_Optimizer->SetGlobalWarningDisplay(false); 35 | m_Optimizer->SetTrace(false); 36 | m_Optimizer->MinimizeOn(); 37 | 38 | m_ElapsedTime = 0; 39 | m_Logger = itk::Logger::New(); 40 | } 41 | //---------------------------------------------------------------------------- 42 | // 43 | template 44 | const typename ShapeModelRegistrationMethodBase::OutputMeshType * 45 | ShapeModelRegistrationMethodBase::GetOutput() const 46 | { 47 | return static_cast(this->ProcessObject::GetOutput(0)); 48 | } 49 | //---------------------------------------------------------------------------- 50 | // 51 | template 52 | itk::ModifiedTimeType ShapeModelRegistrationMethodBase::GetMTime() const 53 | { 54 | itk::ModifiedTimeType mtime = Superclass::GetMTime(); 55 | itk::ModifiedTimeType m; 56 | 57 | if (m_ShapeModel) { 58 | m = m_ShapeModel->GetMTime(); 59 | mtime = (m > mtime ? m : mtime); 60 | } 61 | 62 | if (m_SpatialTransform) { 63 | m = m_SpatialTransform->GetMTime(); 64 | mtime = (m > mtime ? m : mtime); 65 | } 66 | 67 | return mtime; 68 | } 69 | //---------------------------------------------------------------------------- 70 | template 71 | void ShapeModelRegistrationMethodBase::Initialize() 72 | { 73 | if (!m_ShapeModel) { 74 | itkExceptionMacro(<< "Shape model is not initialized."); 75 | } 76 | 77 | if (!m_SpatialTransform) { 78 | itkExceptionMacro(<< "Spatial transform does not initialized."); 79 | } 80 | 81 | if (m_SpatialTransform->GetNumberOfParameters() != m_SpatialScales.size()) { 82 | itkExceptionMacro(<< "the number of parameters of spatial transform and size of spatial scales are not equal"); 83 | } 84 | } 85 | //---------------------------------------------------------------------------- 86 | template 87 | void ShapeModelRegistrationMethodBase::InitializeTransform() 88 | { 89 | // shape model multi transform 90 | m_Transform = ShapeModelMultiTransformType::New(); 91 | m_Transform->SetShapeModel(m_ShapeModel); 92 | m_Transform->SetSpatialTransform(m_SpatialTransform); 93 | 94 | // define scales and bounds 95 | m_Scales.set_size(m_Transform->GetNumberOfParameters()); 96 | 97 | size_t count = 0; 98 | 99 | for (size_t n = 0; n < m_ShapeModel->GetNumberOfPrincipalComponents(); ++n, ++count) { 100 | m_Scales[count] = m_ModelScale; 101 | } 102 | 103 | for (size_t n = 0; n < m_SpatialScales.Size(); ++n, ++count) { 104 | m_Scales[count] = m_SpatialScales[n]; 105 | } 106 | 107 | // define inverse scales 108 | m_InverseScales.set_size(m_Scales.Size()); 109 | 110 | for (size_t n = 0; n < m_Scales.Size(); ++n) { 111 | m_InverseScales[n] = 1 / m_Scales[n]; 112 | } 113 | 114 | m_InitialParameters = m_Transform->GetParameters(); 115 | } 116 | //---------------------------------------------------------------------------- 117 | // initialize optimizer 118 | template 119 | void ShapeModelRegistrationMethodBase::InitializeOptimizer() 120 | { 121 | m_Optimizer->SetCostFunction(m_Metric); 122 | m_Optimizer->SetInitialPosition(m_Transform->GetParameters()); 123 | m_Optimizer->SetScales(m_InverseScales); 124 | m_Optimizer->SetMaximumNumberOfFunctionEvaluations(m_NumberOfIterations); 125 | } 126 | //---------------------------------------------------------------------------- 127 | // perform registration 128 | template 129 | void ShapeModelRegistrationMethodBase::PerformRegistration() 130 | { 131 | // compute multi-stage data 132 | this->ComputeMultiStageData(); 133 | 134 | // perform multi-stage registration 135 | for (size_t n = 0; n < m_NumberOfUsedComponents.GetSize(); ++n) { 136 | m_Transform->SetNumberOfUsedComponents(m_NumberOfUsedComponents[n]); 137 | m_Optimizer->SetInitialPosition(m_Transform->GetParameters()); 138 | try { 139 | m_Optimizer->StartOptimization(); 140 | } 141 | catch (itk::ExceptionObject& excep) { 142 | m_Message.str(""); 143 | m_Message << excep << std::endl; 144 | m_Logger->Fatal(m_Message.str()); 145 | 146 | m_CostFunctionValues[n] = m_Optimizer->GetValue(); 147 | m_Transform->SetParameters(m_Optimizer->GetCurrentPosition()); 148 | break; 149 | } 150 | 151 | m_CostFunctionValues[n] = m_Optimizer->GetValue(); 152 | m_Transform->SetParameters(m_Optimizer->GetCurrentPosition()); 153 | } 154 | } 155 | //---------------------------------------------------------------------------- 156 | // initialize data for multi-stage optimization 157 | template 158 | void ShapeModelRegistrationMethodBase::ComputeMultiStageData() 159 | { 160 | if (m_UseNumberOfEpochs) { 161 | if (m_NumberOfEpochs == 0) { 162 | m_Step = 1; 163 | m_NumberOfEpochs = m_ShapeModel->GetNumberOfPrincipalComponents(); 164 | } 165 | else if (m_NumberOfEpochs > 1) { 166 | m_Step = itk::Math::Ceil(double(m_ShapeModel->GetNumberOfPrincipalComponents()) / (m_NumberOfEpochs - 1)); 167 | m_NumberOfEpochs = itk::Math::Ceil(double(m_ShapeModel->GetNumberOfPrincipalComponents()) / m_Step) + 1; 168 | } 169 | } 170 | else { 171 | if (m_Step == 0) { 172 | m_NumberOfEpochs = 1; 173 | } 174 | else { 175 | m_NumberOfEpochs = itk::Math::Ceil(double(m_ShapeModel->GetNumberOfPrincipalComponents()) / m_Step) + 1; 176 | } 177 | } 178 | 179 | m_NumberOfUsedComponents.SetSize(m_NumberOfEpochs); 180 | 181 | for (size_t n = 0; n < (m_NumberOfEpochs - 1); ++n) { 182 | m_NumberOfUsedComponents[n] = n * m_Step; 183 | } 184 | m_NumberOfUsedComponents[m_NumberOfEpochs - 1] = m_ShapeModel->GetNumberOfPrincipalComponents(); 185 | 186 | m_CostFunctionValues.SetSize(m_NumberOfEpochs); 187 | m_CostFunctionValues.Fill(NAN); 188 | } 189 | //---------------------------------------------------------------------------- 190 | // 191 | template 192 | void ShapeModelRegistrationMethodBase::GenerateOutputData() 193 | { 194 | // compute transformed mesh 195 | typedef itk::TransformMeshFilter TransformFilterType; 196 | auto transform = TransformFilterType::New(); 197 | transform->SetInput(m_ShapeModel->GetRepresenter()->GetReference()); 198 | transform->SetTransform(m_Transform); 199 | try { 200 | transform->Update(); 201 | } 202 | catch (itk::ExceptionObject & excep) { 203 | this->ExceptionHandler(excep, __LINE__); 204 | } 205 | 206 | // connect to the output 207 | this->SetNthOutput(0, transform->GetOutput()); 208 | 209 | m_Time.Stop(); 210 | m_ElapsedTime = m_Time.GetTotal(); 211 | } 212 | //---------------------------------------------------------------------------- 213 | template 214 | void ShapeModelRegistrationMethodBase::PrintReport() 215 | { 216 | std::cout << this->GetNameOfClass() << std::endl; 217 | std::cout << std::endl; 218 | 219 | std::cout << "shape model info" << std::endl; 220 | std::cout << "number of components " << m_ShapeModel->GetNumberOfPrincipalComponents() << std::endl; 221 | std::cout << "number of points " << m_ShapeModel->GetRepresenter()->GetReference()->GetNumberOfPoints() << std::endl; 222 | std::cout << "number of cells " << m_ShapeModel->GetRepresenter()->GetReference()->GetNumberOfCells() << std::endl; 223 | std::cout << std::endl; 224 | 225 | if (m_NumberOfIterations > 0) { 226 | std::cout << "stop condition description " << m_Optimizer->GetStopConditionDescription() << std::endl; 227 | std::cout << "number of used components " << m_NumberOfUsedComponents << std::endl; 228 | std::cout << "cost function values " << m_CostFunctionValues << std::endl; 229 | std::cout << std::endl; 230 | } 231 | 232 | std::cout << m_Transform->GetTransformTypeAsString() << ", " << m_Transform->GetTransformCategory() << std::endl; 233 | std::cout << "number of parameters " << m_Transform->GetNumberOfParameters() << std::endl; 234 | std::cout << std::endl; 235 | std::cout << "initial position" << std::endl << m_InitialParameters << std::endl; 236 | std::cout << "final position" << std::endl << m_Transform->GetParameters() << std::endl; 237 | std::cout << std::endl; 238 | std::cout << "Elapsed time " << m_ElapsedTime << std::endl; 239 | std::cout << std::endl; 240 | } 241 | //---------------------------------------------------------------------------- 242 | template 243 | void ShapeModelRegistrationMethodBase::ExceptionHandler(itk::ExceptionObject & excep, const size_t & line) 244 | { 245 | std::cout << "Error: " << this->GetNameOfClass() << std::endl; 246 | std::cout << "File: " << __FILE__ << ", line " << line << std::endl; 247 | std::cout << excep << std::endl; 248 | } 249 | //---------------------------------------------------------------------------- 250 | } 251 | -------------------------------------------------------------------------------- /Modules/SSM/ssmInitializeSpatialTransform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ssm 10 | { 11 | template 12 | class InitializeSpatialTransform : public itk::Object 13 | { 14 | public: 15 | /** Standard class typedefs. */ 16 | typedef InitializeSpatialTransform Self; 17 | typedef itk::Object Superclass; 18 | typedef itk::SmartPointer Pointer; 19 | typedef itk::SmartPointer ConstPointer; 20 | 21 | /** Method for creation through the object factory. */ 22 | itkNewMacro(Self); 23 | itkTypeMacro(InitializeSpatialTransform, itk::Object); 24 | 25 | enum class Transform 26 | { 27 | Translation, 28 | Euler3D, 29 | Similarity, 30 | ScaleSkewVersor3D 31 | }; 32 | 33 | /** typedefs */ 34 | itkStaticConstMacro(Dimension, unsigned int, 3U); 35 | 36 | typedef typename itk::Transform TransformType; 37 | typedef typename TransformType::InputPointType InputPointType; 38 | typedef typename TransformType::OutputVectorType OutputVectorType; 39 | typedef itk::Array ParametersType; 40 | typedef itk::Array ModeBoundsType; 41 | 42 | // Set logger 43 | itkSetObjectMacro(Logger, itk::Logger); 44 | 45 | // Get transform 46 | itkGetObjectMacro(Transform, TransformType); 47 | 48 | // Set/Get type of transform 49 | itkSetEnumMacro(TransformType, Transform); 50 | itkGetEnumMacro(TransformType, Transform); 51 | void SetTransformType(const size_t & type) { this->SetTransformType(static_cast(type)); } 52 | 53 | itkSetMacro(RotationScale, double); 54 | itkGetMacro(RotationScale, double); 55 | 56 | itkSetMacro(TranslationScale, double); 57 | itkGetMacro(TranslationScale, double); 58 | 59 | itkSetMacro(ScalingScale, double); 60 | itkGetMacro(ScalingScale, double); 61 | 62 | itkSetMacro(SkewScale, double); 63 | itkGetMacro(SkewScale, double); 64 | 65 | // Get scales and bounds 66 | itkGetMacro(Scales, ParametersType); 67 | itkGetMacro(ModeBounds, ModeBoundsType); 68 | itkGetMacro(LowerBounds, ParametersType); 69 | itkGetMacro(UpperBounds, ParametersType); 70 | 71 | // Set/Get center and translation 72 | itkGetMacro(Center, InputPointType); 73 | itkSetMacro(Center, InputPointType); 74 | itkGetMacro(Translation, OutputVectorType); 75 | itkSetMacro(Translation, OutputVectorType); 76 | 77 | void Initialize() 78 | { 79 | switch (m_TransformType) { 80 | case Transform::Translation: { 81 | // Translation transform 82 | typedef typename itk::TranslationTransform TranslationTransformType; 83 | auto transform = TranslationTransformType::New(); 84 | transform->Translate(m_Translation); 85 | m_Transform = transform; 86 | 87 | this->Allocate(); 88 | 89 | // define scales 90 | m_NumberOfTranslationComponents = 3; 91 | 92 | size_t count = 0; 93 | 94 | for (size_t i = 0; i < m_NumberOfTranslationComponents; ++i, ++count) { 95 | m_Scales[count] = m_TranslationScale; 96 | m_ModeBounds[count] = 0; 97 | } 98 | 99 | break; 100 | } 101 | case Transform::Euler3D:{ 102 | // Euler3DTransform 103 | typedef itk::Euler3DTransform Euler3DTransformType; 104 | auto transform = Euler3DTransformType::New(); 105 | transform->SetIdentity(); 106 | transform->SetCenter(m_Center); 107 | transform->SetTranslation(m_Translation); 108 | m_Transform = transform; 109 | 110 | this->Allocate(); 111 | 112 | // define scales 113 | m_NumberOfRotationComponents = 3; 114 | m_NumberOfTranslationComponents = 3; 115 | 116 | size_t count = 0; 117 | 118 | for (size_t i = 0; i < m_NumberOfRotationComponents; ++i, ++count) { 119 | m_Scales[count] = m_RotationScale; 120 | m_ModeBounds[count] = 2; 121 | } 122 | 123 | for (size_t i = 0; i < m_NumberOfTranslationComponents; ++i, ++count) { 124 | m_Scales[count] = m_TranslationScale; 125 | m_ModeBounds[count] = 0; 126 | } 127 | 128 | break; 129 | } 130 | 131 | case Transform::Similarity:{ 132 | // Similarity3DTransform 133 | typedef itk::Similarity3DTransform Similarity3DTransformType; 134 | auto transform = Similarity3DTransformType::New(); 135 | transform->SetIdentity(); 136 | transform->SetCenter(m_Center); 137 | transform->SetTranslation(m_Translation); 138 | m_Transform = transform; 139 | 140 | this->Allocate(); 141 | 142 | // define scales 143 | m_NumberOfRotationComponents = 3; 144 | m_NumberOfTranslationComponents = 3; 145 | m_NumberOfScalingComponents = 1; 146 | 147 | size_t count = 0; 148 | 149 | for (size_t i = 0; i < m_NumberOfRotationComponents; ++i, ++count) { 150 | m_Scales[count] = m_RotationScale; 151 | m_ModeBounds[count] = 2; 152 | } 153 | 154 | for (size_t i = 0; i < m_NumberOfTranslationComponents; ++i, ++count) { 155 | m_Scales[count] = m_TranslationScale; 156 | m_ModeBounds[count] = 0; 157 | } 158 | 159 | for (size_t i = 0; i < m_NumberOfScalingComponents; ++i, ++count) { 160 | m_Scales[count] = m_ScalingScale; 161 | m_ModeBounds[count] = 2; 162 | } 163 | 164 | break; 165 | } 166 | 167 | case Transform::ScaleSkewVersor3D:{ 168 | typedef itk::ScaleSkewVersor3DTransform ScaleSkewVersor3DTransformType; 169 | auto transform = ScaleSkewVersor3DTransformType::New(); 170 | transform->SetIdentity(); 171 | transform->SetCenter(m_Center); 172 | transform->SetTranslation(m_Translation); 173 | m_Transform = transform; 174 | 175 | this->Allocate(); 176 | 177 | // define scales 178 | m_NumberOfRotationComponents = 3; 179 | m_NumberOfTranslationComponents = 3; 180 | m_NumberOfScalingComponents = 3; 181 | m_NumberOfSkewComponents = 6; 182 | 183 | size_t count = 0; 184 | 185 | for (size_t i = 0; i < m_NumberOfRotationComponents; ++i, ++count) { 186 | m_Scales[count] = m_RotationScale; 187 | m_ModeBounds[count] = 2; 188 | } 189 | 190 | for (size_t i = 0; i < m_NumberOfTranslationComponents; ++i, ++count) { 191 | m_Scales[count] = m_TranslationScale; 192 | m_ModeBounds[count] = 0; 193 | } 194 | 195 | for (size_t i = 0; i < m_NumberOfScalingComponents; ++i, ++count) { 196 | m_Scales[count] = m_ScalingScale; 197 | m_ModeBounds[count] = 2; 198 | } 199 | 200 | for (size_t i = 0; i < m_NumberOfSkewComponents; ++i, ++count) { 201 | m_Scales[count] = m_SkewScale; 202 | m_ModeBounds[count] = 2; 203 | } 204 | 205 | break; 206 | } 207 | } 208 | 209 | for (size_t n = 0; n < m_NumberOfParameters; ++n) { 210 | if (m_ModeBounds[n] == 1 || m_ModeBounds[n] == 2) { 211 | m_LowerBounds[n] = m_Transform->GetParameters()[n] - m_Scales[n]; 212 | } 213 | if (m_ModeBounds[n] == 2 || m_ModeBounds[n] == 3) { 214 | m_UpperBounds[n] = m_Transform->GetParameters()[n] + m_Scales[n]; 215 | } 216 | } 217 | } 218 | 219 | void PrintReport() const 220 | { 221 | std::cout << this->GetNameOfClass() << std::endl; 222 | std::cout << "transform " << m_Transform->GetTransformTypeAsString() << std::endl; 223 | std::cout << "center " << m_Center << std::endl; 224 | std::cout << "translation " << m_Translation << std::endl; 225 | std::cout << "fixed parameters " << m_Transform->GetFixedParameters() << ", " << m_Transform->GetNumberOfFixedParameters() << std::endl; 226 | std::cout << "parameters " << m_Transform->GetParameters() << ", " << m_Transform->GetNumberOfParameters() << std::endl; 227 | std::cout << std::endl; 228 | std::cout << "scales " << m_Scales << std::endl; 229 | std::cout << "mode bounds " << m_ModeBounds << std::endl; 230 | std::cout << "lower bounds " << m_LowerBounds << std::endl; 231 | std::cout << "upper bounds " << m_UpperBounds << std::endl; 232 | std::cout << std::endl; 233 | } 234 | 235 | private: 236 | typename TransformType::Pointer m_Transform = nullptr; 237 | Transform m_TransformType = Transform::Similarity; 238 | InputPointType m_Center; 239 | OutputVectorType m_Translation; 240 | 241 | /** Set the boundary condition for each variable, where 242 | * select[i] = 0 if x[i] is unbounded, 243 | * = 1 if x[i] has only a lower bound, 244 | * = 2 if x[i] has both lower and upper bounds, and 245 | * = 3 if x[1] has only an upper bound */ 246 | ModeBoundsType m_ModeBounds; 247 | ParametersType m_LowerBounds; 248 | ParametersType m_UpperBounds; 249 | ParametersType m_Scales; 250 | 251 | size_t m_NumberOfParameters; 252 | size_t m_NumberOfTranslationComponents; 253 | size_t m_NumberOfRotationComponents; 254 | size_t m_NumberOfScalingComponents; 255 | size_t m_NumberOfSkewComponents; 256 | 257 | double m_TranslationScale = 1; 258 | double m_RotationScale = 0.2; 259 | double m_ScalingScale = 0.2; 260 | double m_SkewScale = 0.2; 261 | 262 | protected: 263 | InitializeSpatialTransform() {} 264 | ~InitializeSpatialTransform() {} 265 | 266 | void Allocate() 267 | { 268 | m_NumberOfParameters = m_Transform->GetNumberOfParameters(); 269 | 270 | m_Scales.set_size(m_NumberOfParameters); 271 | m_Scales.Fill(NAN); 272 | 273 | m_ModeBounds.set_size(m_NumberOfParameters); 274 | m_ModeBounds.Fill(NAN); 275 | 276 | m_LowerBounds.set_size(m_NumberOfParameters); 277 | m_LowerBounds.Fill(NAN); 278 | 279 | m_UpperBounds.set_size(m_NumberOfParameters); 280 | m_UpperBounds.Fill(NAN); 281 | } 282 | 283 | itk::Logger::Pointer m_Logger = itk::Logger::New(); 284 | mutable std::ostringstream m_Message; 285 | }; 286 | } 287 | -------------------------------------------------------------------------------- /Applications/ssm-fit-shapemodel-to-image.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ssmTypes.h" 8 | #include "ssmUtils.h" 9 | #include "ssmPointSetToImageMetrics.h" 10 | #include "ssmInitializeSpatialTransform.h" 11 | #include "ssmShapeModelToImageRegistrationMethod.h" 12 | 13 | struct ProgramOptions 14 | { 15 | bool help; 16 | std::string modelFile; 17 | std::string imageFile; 18 | std::string outputFile; 19 | std::string reportFile; 20 | std::string transformFile; 21 | size_t transform = 2; 22 | size_t iterations = 500; 23 | size_t degree = 2; 24 | double regularization = 0.1; 25 | }; 26 | 27 | namespace po = boost::program_options; 28 | po::options_description initializeProgramOptions(ProgramOptions& poParameters); 29 | 30 | int main(int argc, char** argv) 31 | { 32 | ProgramOptions options; 33 | po::options_description description = initializeProgramOptions(options); 34 | po::variables_map vm; 35 | try { 36 | po::parsed_options parsedOptions = po::command_line_parser(argc, argv).options(description).run(); 37 | po::store(parsedOptions, vm); 38 | po::notify(vm); 39 | } 40 | catch (po::error& e) { 41 | cerr << "An exception occurred while parsing the command line:" << endl; 42 | cerr << e.what() << endl << endl; 43 | cout << description << endl; 44 | return EXIT_FAILURE; 45 | } 46 | if (options.help == true) { 47 | cout << description << endl; 48 | return EXIT_SUCCESS; 49 | } 50 | 51 | //---------------------------------------------------------------------------- 52 | // read image 53 | BinaryImageType::Pointer image = BinaryImageType::New(); 54 | if (!readImage(image, options.imageFile)) { 55 | return EXIT_FAILURE; 56 | } 57 | std::cout << "image " << options.imageFile << std::endl; 58 | std::cout << "size " << image->GetLargestPossibleRegion().GetSize() << std::endl; 59 | std::cout << "spacing " << image->GetSpacing() << std::endl; 60 | std::cout << std::endl; 61 | 62 | //---------------------------------------------------------------------------- 63 | // read statistical shape model 64 | typedef itk::StandardMeshRepresenter RepresenterType; 65 | RepresenterType::Pointer representer = RepresenterType::New(); 66 | 67 | typedef itk::StatisticalModel StatisticalModelType; 68 | StatisticalModelType::Pointer model = StatisticalModelType::New(); 69 | try { 70 | model->Load(representer, options.modelFile.c_str()); 71 | } 72 | catch (itk::ExceptionObject & excp) { 73 | std::cerr << excp << std::endl; 74 | return EXIT_FAILURE; 75 | } 76 | 77 | std::cout << "model " << options.modelFile << std::endl; 78 | std::cout << "number of components " << model->GetNumberOfPrincipalComponents() << std::endl; 79 | std::cout << "number of points " << model->GetRepresenter()->GetReference()->GetNumberOfPoints() << std::endl; 80 | std::cout << std::endl; 81 | 82 | //---------------------------------------------------------------------------- 83 | // initialize spatial transform 84 | MeshType::BoundingBoxType::ConstPointer boundingBox = model->DrawMean()->GetBoundingBox(); 85 | BinaryImageType::SpacingType spacing(1); 86 | BinaryImageType::PointType origin = boundingBox->GetMinimum(); 87 | BinaryImageType::SizeType size; 88 | for (size_t n = 0; n < Dimension; ++n) { 89 | size[n] = (boundingBox->GetMaximum()[n] - boundingBox->GetMinimum()[n]) / spacing[n]; 90 | } 91 | 92 | typedef itk::TriangleMeshToBinaryImageFilter ShapeToBinaryImageFilterType; 93 | ShapeToBinaryImageFilterType::Pointer shapeToImage = ShapeToBinaryImageFilterType::New(); 94 | shapeToImage->SetInput(model->DrawMean()); 95 | shapeToImage->SetSize(size); 96 | shapeToImage->SetOrigin(origin); 97 | shapeToImage->SetSpacing(spacing); 98 | shapeToImage->SetOutsideValue(0); 99 | shapeToImage->SetInsideValue(1); 100 | try { 101 | shapeToImage->Update(); 102 | } 103 | catch (itk::ExceptionObject& excep) { 104 | std::cout << excep << std::endl; 105 | return EXIT_FAILURE; 106 | } 107 | 108 | // moment calculators 109 | typedef itk::ImageMomentsCalculator ImageCalculatorType; 110 | ImageCalculatorType::Pointer movingCalculator = ImageCalculatorType::New(); 111 | movingCalculator->SetImage(shapeToImage->GetOutput()); 112 | movingCalculator->Compute(); 113 | 114 | ImageCalculatorType::Pointer fixedCalculator = ImageCalculatorType::New(); 115 | fixedCalculator->SetImage(image); 116 | fixedCalculator->Compute(); 117 | 118 | typedef ImageCalculatorType::VectorType VectorType; 119 | VectorType center = movingCalculator->GetCenterOfGravity(); 120 | VectorType translation = fixedCalculator->GetCenterOfGravity() - movingCalculator->GetCenterOfGravity(); 121 | 122 | // initialize spatial transform 123 | typedef ssm::InitializeSpatialTransform InitializeSpatialTransformType; 124 | auto initializer = InitializeSpatialTransformType::New(); 125 | initializer->SetCenter(center); 126 | initializer->SetTranslation(translation); 127 | initializer->SetTransformType(options.transform); 128 | try { 129 | initializer->Initialize(); 130 | } 131 | catch (itk::ExceptionObject& excep) { 132 | std::cout << excep << std::endl; 133 | return EXIT_FAILURE; 134 | } 135 | initializer->PrintReport(); 136 | 137 | //---------------------------------------------------------------------------- 138 | // perform shape model to image registration 139 | typedef ssm::ShapeModelToImageRegistrationMethod ShapeModelRegistrationMethodType; 140 | auto shapeModelToSurfaceRegistration = ShapeModelRegistrationMethodType::New(); 141 | shapeModelToSurfaceRegistration->SetShapeModel(model); 142 | shapeModelToSurfaceRegistration->SetImage(image); 143 | shapeModelToSurfaceRegistration->SetComputeLevelSetImage(true); 144 | shapeModelToSurfaceRegistration->SetNumberOfIterations(options.iterations); 145 | shapeModelToSurfaceRegistration->SetSpatialTransform(initializer->GetTransform()); 146 | shapeModelToSurfaceRegistration->SetSpatialScales(initializer->GetScales()); 147 | shapeModelToSurfaceRegistration->GetMetric()->SetRegularizationParameter(options.regularization); 148 | shapeModelToSurfaceRegistration->GetMetric()->SetDegree(options.degree); 149 | shapeModelToSurfaceRegistration->SetModelScale(3); 150 | shapeModelToSurfaceRegistration->SetNumberOfEpochs(1); 151 | try { 152 | shapeModelToSurfaceRegistration->Update(); 153 | } 154 | catch (itk::ExceptionObject& excep) { 155 | std::cerr << excep << std::endl; 156 | return EXIT_FAILURE; 157 | } 158 | shapeModelToSurfaceRegistration->PrintReport(); 159 | 160 | // perform registration 161 | typedef std::pair PairType; 162 | std::vector info; 163 | info.push_back(PairType("Metric", std::to_string(shapeModelToSurfaceRegistration->GetOptimizer()->GetValue()))); 164 | info.push_back(PairType("Elapsed time", std::to_string(shapeModelToSurfaceRegistration->GetElapsedTime()))); 165 | 166 | // compute metrics 167 | typedef itk::PointSet PointSetType; 168 | typedef ssm::PointSetToImageMetrics PointSetToImageMetricsType; 169 | PointSetToImageMetricsType::Pointer metrics = PointSetToImageMetricsType::New(); 170 | metrics->SetPointSetAsMesh(shapeModelToSurfaceRegistration->GetOutput()); 171 | metrics->SetImage(shapeModelToSurfaceRegistration->GetLevelSetImage()); 172 | metrics->SetInfo(info); 173 | metrics->Compute(); 174 | metrics->PrintReport(); 175 | 176 | // write report to *.csv file 177 | std::cout << "print report to the file: " << options.reportFile << std::endl; 178 | metrics->PrintReportToFile(options.reportFile, getBaseNameFromPath(options.imageFile)); 179 | 180 | // write surface 181 | std::cout << "write output surface to the file: " << options.outputFile << std::endl; 182 | if (!writeMesh(shapeModelToSurfaceRegistration->GetOutput(), options.outputFile)) { 183 | return EXIT_FAILURE; 184 | } 185 | 186 | // write transform 187 | if (options.transformFile != "") { 188 | std::cout << "write transform to the file: " << options.transformFile << std::endl; 189 | writeTransform(initializer->GetTransform(), options.transformFile); 190 | } 191 | 192 | return EXIT_SUCCESS; 193 | } 194 | 195 | po::options_description initializeProgramOptions(ProgramOptions& options) 196 | { 197 | po::options_description mandatory("Mandatory options"); 198 | mandatory.add_options() 199 | ("model,m", po::value(&options.modelFile), "The path to the input shape model file.") 200 | ("image,i", po::value(&options.imageFile), "The path to the input image file.") 201 | ("output,o", po::value(&options.outputFile), "The path for the output surface file.") 202 | ; 203 | 204 | po::options_description input("Optional input options"); 205 | input.add_options() 206 | ("transform", po::value(&options.transform)->default_value(options.transform), "The type of the used spatial transform.") 207 | ("iterations", po::value(&options.iterations)->default_value(options.iterations), "The number of iterations.") 208 | ("degree", po::value(&options.degree)->default_value(options.degree), "The degree of residuals to compute shape model to image metric.") 209 | ("regularization", po::value(&options.regularization)->default_value(options.regularization), "The regularization factor.") 210 | ; 211 | 212 | po::options_description output("Optional output options"); 213 | output.add_options() 214 | ("output-transform", po::value(&options.transformFile), "The path for the output transform file.") 215 | ; 216 | 217 | po::options_description report("Optional report options"); 218 | report.add_options() 219 | ("report,r", po::value(&options.reportFile), "The path for the file to print report.") 220 | ; 221 | 222 | po::options_description help("Optional options"); 223 | help.add_options() 224 | ("help,h", po::bool_switch(&options.help), "Display this help message") 225 | ; 226 | 227 | po::options_description description; 228 | description.add(mandatory).add(input).add(output).add(report).add(help); 229 | 230 | return description; 231 | } 232 | -------------------------------------------------------------------------------- /Applications/ssm-fit-shapemodel-to-surface.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ssmTypes.h" 8 | #include "ssmUtils.h" 9 | #include "ssmPointSetToImageMetrics.h" 10 | #include "ssmShapeModelToImageRegistrationMethod.h" 11 | #include "ssmMeshToLevelSetImageFilter.h" 12 | #include "ssmInitializeSpatialTransform.h" 13 | 14 | struct ProgramOptions 15 | { 16 | bool help; 17 | std::string modelFile; 18 | std::string surfaceFile; 19 | std::string outputFile; 20 | std::string levelsetFile; 21 | std::string reportFile; 22 | std::string transformFile; 23 | size_t transform = 2; 24 | size_t iterations = 500; 25 | size_t degree = 2; 26 | double regularization = 0.1; 27 | }; 28 | 29 | namespace po = boost::program_options; 30 | po::options_description initializeProgramOptions(ProgramOptions& poParameters); 31 | 32 | int main(int argc, char** argv) 33 | { 34 | ProgramOptions options; 35 | po::options_description description = initializeProgramOptions(options); 36 | po::variables_map vm; 37 | try { 38 | po::parsed_options parsedOptions = po::command_line_parser(argc, argv).options(description).run(); 39 | po::store(parsedOptions, vm); 40 | po::notify(vm); 41 | } 42 | catch (po::error& e) { 43 | cerr << "An exception occurred while parsing the command line:" << endl; 44 | cerr << e.what() << endl << endl; 45 | cout << description << endl; 46 | return EXIT_FAILURE; 47 | } 48 | if (options.help == true) { 49 | cout << description << endl; 50 | return EXIT_SUCCESS; 51 | } 52 | 53 | //---------------------------------------------------------------------------- 54 | // read surface 55 | MeshType::Pointer surface = MeshType::New(); 56 | if (!readMesh(surface, options.surfaceFile)) { 57 | return EXIT_FAILURE; 58 | } 59 | std::cout << "surface " << surface << std::endl; 60 | std::cout << "number of cells " << surface->GetNumberOfCells() << std::endl; 61 | std::cout << "number of points " << surface->GetNumberOfPoints() << std::endl; 62 | std::cout << std::endl; 63 | 64 | //---------------------------------------------------------------------------- 65 | // read statistical shape model 66 | typedef itk::StandardMeshRepresenter RepresenterType; 67 | RepresenterType::Pointer representer = RepresenterType::New(); 68 | 69 | typedef itk::StatisticalModel StatisticalModelType; 70 | StatisticalModelType::Pointer model = StatisticalModelType::New(); 71 | try { 72 | model->Load(representer, options.modelFile.c_str()); 73 | } 74 | catch (itk::ExceptionObject & excp) { 75 | std::cerr << excp << std::endl; 76 | return EXIT_FAILURE; 77 | } 78 | 79 | std::cout << "model " << options.modelFile << std::endl; 80 | std::cout << "number of components " << model->GetNumberOfPrincipalComponents() << std::endl; 81 | std::cout << "number of points " << model->GetRepresenter()->GetReference()->GetNumberOfPoints() << std::endl; 82 | std::cout << std::endl; 83 | 84 | //---------------------------------------------------------------------------- 85 | // compute level set image 86 | double margin = 0.10; 87 | BinaryImageType::SpacingType spacing(1); 88 | 89 | MeshType::BoundingBoxType::ConstPointer boundingBox = model->DrawMean()->GetBoundingBox(); 90 | BinaryImageType::PointType origin = boundingBox->GetMinimum(); 91 | BinaryImageType::SizeType size; 92 | for (size_t n = 0; n < Dimension; ++n) { 93 | size[n] = (boundingBox->GetMaximum()[n] - boundingBox->GetMinimum()[n]) / spacing[n]; 94 | } 95 | 96 | typedef itk::TriangleMeshToBinaryImageFilter ShapeToBinaryImageFilterType; 97 | ShapeToBinaryImageFilterType::Pointer shapeToImage = ShapeToBinaryImageFilterType::New(); 98 | shapeToImage->SetInput(model->DrawMean()); 99 | shapeToImage->SetSize(size); 100 | shapeToImage->SetOrigin(origin); 101 | shapeToImage->SetSpacing(spacing); 102 | shapeToImage->SetOutsideValue(0); 103 | shapeToImage->SetInsideValue(1); 104 | try { 105 | shapeToImage->Update(); 106 | } 107 | catch (itk::ExceptionObject& excep) { 108 | std::cout << excep << std::endl; 109 | return EXIT_FAILURE; 110 | } 111 | 112 | // initialize spatial transform 113 | typedef itk::ImageMomentsCalculator ImageCalculatorType; 114 | ImageCalculatorType::Pointer movingCalculator = ImageCalculatorType::New(); 115 | movingCalculator->SetImage(shapeToImage->GetOutput()); 116 | movingCalculator->Compute(); 117 | 118 | ImageCalculatorType::Pointer fixedCalculator = ImageCalculatorType::New(); 119 | fixedCalculator->SetImage(shapeToImage->GetOutput()); 120 | fixedCalculator->Compute(); 121 | 122 | typedef ImageCalculatorType::VectorType VectorType; 123 | VectorType center = movingCalculator->GetCenterOfGravity(); 124 | VectorType translation = fixedCalculator->GetCenterOfGravity() - movingCalculator->GetCenterOfGravity(); 125 | 126 | typedef ssm::InitializeSpatialTransform TransformInitializerType; 127 | TransformInitializerType::Pointer initializer = TransformInitializerType::New(); 128 | initializer->SetTransformType(options.transform); 129 | initializer->SetCenter(center); 130 | initializer->SetTranslation(translation); 131 | try { 132 | initializer->Initialize(); 133 | } 134 | catch (itk::ExceptionObject& excep) { 135 | std::cout << excep << std::endl; 136 | return EXIT_FAILURE; 137 | } 138 | initializer->PrintReport(); 139 | 140 | //---------------------------------------------------------------------------- 141 | // perform shape model to image registration 142 | typedef ssm::ShapeModelToImageRegistrationMethod RegistrationMethodType; 143 | auto shapeModelToSurfaceRegistration = RegistrationMethodType::New(); 144 | shapeModelToSurfaceRegistration->SetShapeModel(model); 145 | shapeModelToSurfaceRegistration->SetImage(shapeToImage->GetOutput()); 146 | shapeModelToSurfaceRegistration->SetNumberOfIterations(options.iterations); 147 | shapeModelToSurfaceRegistration->SetSpatialTransform(initializer->GetTransform()); 148 | shapeModelToSurfaceRegistration->SetSpatialScales(initializer->GetScales()); 149 | shapeModelToSurfaceRegistration->GetMetric()->SetRegularizationParameter(options.regularization); 150 | shapeModelToSurfaceRegistration->GetMetric()->SetDegree(options.degree); 151 | try { 152 | shapeModelToSurfaceRegistration->Update(); 153 | } 154 | catch (itk::ExceptionObject& excep) { 155 | std::cerr << excep << std::endl; 156 | return EXIT_FAILURE; 157 | } 158 | shapeModelToSurfaceRegistration->PrintReport(); 159 | 160 | typedef std::pair PairType; 161 | std::vector info; 162 | info.push_back(PairType("Metric", std::to_string(shapeModelToSurfaceRegistration->GetOptimizer()->GetValue()))); 163 | 164 | //---------------------------------------------------------------------------- 165 | // compute metrics 166 | typedef itk::PointSet PointSetType; 167 | typedef ssm::PointSetToImageMetrics PointSetToImageMetricsType; 168 | PointSetToImageMetricsType::Pointer metrics = PointSetToImageMetricsType::New(); 169 | metrics->SetPointSetAsMesh(shapeModelToSurfaceRegistration->GetOutput()); 170 | metrics->SetImage(shapeModelToSurfaceRegistration->GetLevelSetImage()); 171 | metrics->SetInfo(info); 172 | metrics->Compute(); 173 | metrics->PrintReport(); 174 | 175 | // write report to *.csv file 176 | if (options.reportFile != "") { 177 | metrics->PrintReportToFile(options.reportFile, getBaseNameFromPath(options.surfaceFile)); 178 | } 179 | 180 | // write surface 181 | if (!writeMesh(shapeModelToSurfaceRegistration->GetOutput(), options.outputFile)) { 182 | return EXIT_FAILURE; 183 | } 184 | 185 | // write levelset image 186 | if (options.levelsetFile != "" ) { 187 | if (!writeImage(shapeModelToSurfaceRegistration->GetLevelSetImage(), options.reportFile)) { 188 | return EXIT_FAILURE; 189 | } 190 | } 191 | 192 | // write transform 193 | if (options.transformFile != "") { 194 | std::cout << "write transform to the file: " << options.transformFile << std::endl; 195 | writeTransform(initializer->GetTransform(), options.transformFile); 196 | } 197 | 198 | return EXIT_SUCCESS; 199 | } 200 | 201 | po::options_description initializeProgramOptions(ProgramOptions& options) 202 | { 203 | po::options_description mandatory("Mandatory options"); 204 | mandatory.add_options() 205 | ("model,m", po::value(&options.modelFile), "The path to the input shape model file.") 206 | ("surface,s", po::value(&options.surfaceFile), "The path to the input surface file.") 207 | ("output,o", po::value(&options.outputFile), "The path for the output surface file.") 208 | ; 209 | 210 | po::options_description input("Optional input options"); 211 | input.add_options() 212 | ("transform", po::value(&options.transform)->default_value(options.transform), "The type of the used spatial transform.") 213 | ("iterations", po::value(&options.iterations)->default_value(options.iterations), "The number of iterations.") 214 | ("degree", po::value(&options.degree)->default_value(options.degree), "The degree of residuals to compute shape model to image metric.") 215 | ("regularization", po::value(&options.regularization)->default_value(options.regularization), "The regularization factor.") 216 | ; 217 | 218 | po::options_description output("Optional output options"); 219 | output.add_options() 220 | ("output-levelset", po::value(&options.levelsetFile), "The path for the output level-set image.") 221 | ("output-transform", po::value(&options.transformFile), "The path for the output transform file.") 222 | ; 223 | 224 | po::options_description report("Optional report options"); 225 | report.add_options() 226 | ("report,r", po::value(&options.reportFile), "The path for the file to print report.") 227 | ; 228 | 229 | po::options_description help("Optional options"); 230 | help.add_options() 231 | ("help,h", po::bool_switch(&options.help), "Display this help message") 232 | ; 233 | 234 | po::options_description description; 235 | description.add(mandatory).add(input).add(output).add(report).add(help); 236 | 237 | return description; 238 | } 239 | --------------------------------------------------------------------------------