pax_global_header00006660000000000000000000000064147453406510014523gustar00rootroot0000000000000052 comment=2fabde9ab6fe78082f2b1d764840a89d2cce428c elastix-5.2.0/000077500000000000000000000000001474534065100132005ustar00rootroot00000000000000elastix-5.2.0/.clang-format000066400000000000000000000121661474534065100155610ustar00rootroot00000000000000## This config file is only relevant for clang-format version 8.0.0 ## ## Examples of each format style can be found on the in the clang-format documentation ## See: https://clang.llvm.org/docs/ClangFormatStyleOptions.html for details of each option ## ## The clang-format binaries can be downloaded as part of the clang binary distributions ## from http://releases.llvm.org/download.html ## ## Use the script Utilities/Maintenance/clang-format.bash to faciliate ## maintaining a consistent code style. ## ## EXAMPLE apply code style enforcement before commit: # Utilities/Maintenance/clang-format.bash --clang ${PATH_TO_CLANG_FORMAT_8.0.0} --modified ## EXAMPLE apply code style enforcement after commit: # Utilities/Maintenance/clang-format.bash --clang ${PATH_TO_CLANG_FORMAT_8.0.0} --last --- # This configuration requires clang-format version 8.0.0 exactly. BasedOnStyle: Mozilla Language: Cpp AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: true AlignEscapedNewlines: Right AlignOperands: true AlignTrailingComments: true # clang 9.0 AllowAllArgumentsOnNextLine: true # clang 9.0 AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline # clang 9.0 AllowShortLambdasOnASingleLine: All # clang 9.0 features AllowShortIfStatementsOnASingleLine: Never AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: All AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes BinPackArguments: false BinPackParameters: false BreakBeforeBraces: Custom BraceWrapping: # clang 9.0 feature AfterCaseLabel: false AfterClass: true AfterControlStatement: true AfterEnum: true AfterFunction: true AfterNamespace: true AfterObjCDeclaration: true AfterStruct: true AfterUnion: true AfterExternBlock: true BeforeCatch: true BeforeElse: true ## This is the big change from historical ITK formatting! # Historically ITK used a style similar to https://en.wikipedia.org/wiki/Indentation_style#Whitesmiths_style # with indented braces, and not indented code. This style is very difficult to automatically # maintain with code beautification tools. Not indenting braces is more common among # formatting tools. IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false BreakBeforeBinaryOperators: None #clang 6.0 BreakBeforeInheritanceComma: true BreakInheritanceList: BeforeComma BreakBeforeTernaryOperators: true #clang 6.0 BreakConstructorInitializersBeforeComma: true BreakConstructorInitializers: BeforeComma BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ## The following line allows larger lines in non-documentation code ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 2 ContinuationIndentWidth: 2 Cpp11BracedListStyle: false DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 - Regex: '.*' Priority: 1 IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: true IndentPPDirectives: AfterHash IndentWidth: 2 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 2 NamespaceIndentation: None ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: false PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 ## The following line allows larger lines in non-documentation code PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Middle ReflowComments: true # We may want to sort the includes as a separate pass SortIncludes: false # We may want to revisit this later SortUsingDeclarations: false SpaceAfterCStyleCast: false # SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 2 UseTab: Never ... elastix-5.2.0/.editorconfig000066400000000000000000000003041474534065100156520ustar00rootroot00000000000000# EditorConfig: https://EditorConfig.org # This is the top-most EditorConfig file root = true # Apply to any source file [*.*] # Indentation of two spaces indent_style = space indent_size = 2 elastix-5.2.0/.githooks/000077500000000000000000000000001474534065100151055ustar00rootroot00000000000000elastix-5.2.0/.githooks/pre-commit000066400000000000000000000024211474534065100171030ustar00rootroot00000000000000#! /bin/sh diff_files=`git diff --cached --name-only | grep -E '\.cxx$|\.hxx$|\.h$' ` if [ "$(uname)" == "Darwin" ]; then clangFile=.githooks/clangFormatMac if [ -f "$clangFile" ]; then echo "clangFormat already downloaded." else curl -o .githooks/clangFormatMac https://data.kitware.com/api/v1/file/5d274e88877dfcc902effc47/download chmod u+x .githooks/clangFormatMac fi for file in $diff_files; do .githooks/clangFormatMac -style=file -i --verbose $file; done elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then clangFile=.githooks/clangFormatLinux if [ -f "$clangFile" ]; then echo "clangFormat already downloaded." else curl -o .githooks/clangFormatLinux https://data.kitware.com/api/v1/file/5d2b8c87877dfcc902fda594/download chmod u+x .githooks/clangFormatLinux fi for file in $diff_files; do .githooks/clangFormatLinux -style=file -i --verbose $file; done else clangFile=.githooks/clangFormatWindows.exe if [ -f "$clangFile" ]; then echo "clangFormat already downloaded." else curl -o .githooks/clangFormatWindows.exe https://data.kitware.com/api/v1/file/5d2b8775877dfcc902fd8236/download fi for file in $diff_files; do .githooks/clangFormatWindows.exe -style=file -i --verbose $file; done fi git status elastix-5.2.0/.github/000077500000000000000000000000001474534065100145405ustar00rootroot00000000000000elastix-5.2.0/.github/workflows/000077500000000000000000000000001474534065100165755ustar00rootroot00000000000000elastix-5.2.0/.github/workflows/ElastixGitHubActions.yml000066400000000000000000000222661474534065100233650ustar00rootroot00000000000000name: Elastix on: [push, pull_request] jobs: build: # The CMake configure and build commands are platform agnostic and should work equally # well on Windows or Mac. You can convert this to a matrix build if you need # cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix runs-on: ${{ matrix.os }} strategy: max-parallel: 3 matrix: os: [ubuntu-22.04, windows-2022, macos-12] include: - os: ubuntu-22.04 c-compiler: "gcc" cxx-compiler: "g++" itk-git-tag: "v5.4.0" cmake-build-type: "Release" ANNLib: "libANNlib-5.2.so" ANNLib2: "libANNlib-5.2.so.1" - os: windows-2022 c-compiler: "cl.exe" cxx-compiler: "cl.exe" itk-git-tag: "v5.4.0" cmake-build-type: "Release" ANNLib: "ANNlib-5.2.dll" vcvars64: "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat" - os: macos-12 c-compiler: "clang" cxx-compiler: "clang++" itk-git-tag: "v5.4.0" cmake-build-type: "Release" ANNLib: "libANNlib-5.2.1.dylib" ANNLib2: "libANNlib-5.2.dylib" steps: - uses: actions/checkout@v3 - name: Make directory structure run: | items=(*) mkdir Elastix-source mv ${items[*]} Elastix-source mv .editorconfig Elastix-source mv .clang-format Elastix-source mv Elastix-source/Dockerfile . shell: bash - uses: actions/cache@v3 id: cache with: path: | ITK-build ITK-source key: ${{ matrix.itk-git-tag }}-${{ matrix.os }}-${{ matrix.cmake-build-type }} - name: Set up Python uses: actions/setup-python@v3 with: python-version: 3.8 - name: Install build dependencies run: | python -m pip install --upgrade pip python -m pip install ninja - name: Get specific version of CMake, Ninja uses: lukka/get-cmake@v3.24.2 - name: Download ITK if: steps.cache.outputs.cache-hit != 'true' run: | git clone https://github.com/InsightSoftwareConsortium/ITK.git ITK-source cd ITK-source git checkout ${{ matrix.itk-git-tag }} - name: Build ITK if: ${{ steps.cache.outputs.cache-hit != 'true' && !startsWith(matrix.os, 'windows') }} run: | mkdir ITK-build cd ITK-build cmake -DCMAKE_C_COMPILER:FILEPATH="${{ matrix.c-compiler }}" -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}" -DCMAKE_BUILD_TYPE:STRING=${{ matrix.cmake-build-type }} -DBUILD_EXAMPLES=OFF -DBUILD_TESTING:BOOL=OFF -DITK_LEGACY_REMOVE=ON -GNinja ../ITK-source ninja - name: Build ITK if: steps.cache.outputs.cache-hit != 'true' && startsWith(matrix.os, 'windows') run: | mkdir ITK-build cd ITK-build call "${{ matrix.vcvars64 }}" cmake -DCMAKE_C_COMPILER:FILEPATH="${{ matrix.c-compiler }}" -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}" -DCMAKE_BUILD_TYPE:STRING=${{ matrix.cmake-build-type }} -DBUILD_EXAMPLES=OFF -DBUILD_TESTING:BOOL=OFF -GNinja ../ITK-source ninja shell: cmd - name: Install requirements of PythonTests run: | python -m pip install -r ${{ github.workspace }}/Elastix-source/Testing/PythonTests/requirements.txt - name: Configure CTest script shell: bash run: | operating_system="${{ matrix.os }}" cat > dashboard.cmake << EOF set(CTEST_SITE "GitHubActions") file(TO_CMAKE_PATH "\$ENV{GITHUB_WORKSPACE}" CTEST_DASHBOARD_ROOT) file(TO_CMAKE_PATH "\$ENV{GITHUB_WORKSPACE}/Elastix-source" CTEST_SOURCE_DIRECTORY) file(TO_CMAKE_PATH "\$ENV{GITHUB_WORKSPACE}/Elastix-build" CTEST_BINARY_DIRECTORY) set(dashboard_source_name "${GITHUB_REPOSITORY}") set(branch "${GITHUB_REF#refs/heads/}") set(sha "${GITHUB_SHA}") set(dashboard_model "Experimental") set(CTEST_BUILD_NAME "${GITHUB_REPOSITORY}-${operating_system}-\${branch}-\${sha}") set(CTEST_UPDATE_VERSION_ONLY 1) set(CTEST_TEST_ARGS \${CTEST_TEST_ARGS} PARALLEL_LEVEL \${PARALLEL_LEVEL}) set(CTEST_BUILD_CONFIGURATION "Release") set(CTEST_CMAKE_GENERATOR "Ninja") set(dashboard_no_clean 1) set(ENV{CC} ${{ matrix.c-compiler }}) set(ENV{CXX} ${{ matrix.cxx-compiler }}) if(WIN32) set(ENV{PATH} "\${CTEST_DASHBOARD_ROOT}/ITK-build/bin;\$ENV{PATH}") endif() set(dashboard_cache " ITK_DIR:PATH=\${CTEST_DASHBOARD_ROOT}/ITK-build ELASTIX_USE_GTEST:BOOL=ON USE_ALL_COMPONENTS:BOOL=ON BUILD_TESTING:BOOL=ON ") string(TIMESTAMP build_date "%Y-%m-%d") message("CDash Build Identifier: \${build_date} \${CTEST_BUILD_NAME}") message("CTEST_SITE= \${CTEST_SITE}") include(\${CTEST_SOURCE_DIRECTORY}/Testing/Dashboard/elxCommonCDash.cmake) EOF cat dashboard.cmake - name: Test MacOs if: startsWith(matrix.os, 'macos') run: | ctest --output-on-failure -VV -j 2 -E "elastix_run_example_COMPARE_IM|elastix_run_3DCT_lung.MI.bspline.ASGD.001_COMPARE_TP|elastix_run_3DCT_lung.NMI.bspline.ASGD.001_COMPARE_TP" -S dashboard.cmake - name: Test Windows if: startsWith(matrix.os, 'windows') run: | call "${{ matrix.vcvars64 }}" ctest --output-on-failure -VV -j 2 -E "elastix_run_example_COMPARE_IM|elastix_run_3DCT_lung.MI.bspline.ASGD.001_COMPARE_TP" -S dashboard.cmake shell: cmd - name: Test Ubuntu if: startsWith(matrix.os, 'ubuntu') run: | ctest --output-on-failure -VV -j 2 -E "elastix_run_example_COMPARE_IM|elastix_run_3DCT_lung.MI.bspline.ASGD.001_COMPARE_TP" -S dashboard.cmake - name: Build externalproject example if: ${{ !startsWith(matrix.os, 'windows') }} run: | mkdir externalproject-build cd externalproject-build cmake -DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}" -DCMAKE_BUILD_TYPE=${{ matrix.cmake-build-type }} -DElastix_DIR=${{ github.workspace }}/Elastix-build -GNinja ../Elastix-source/dox/externalproject ninja - name: Build externalproject example if: startsWith(matrix.os, 'windows') run: | mkdir externalproject-build cd externalproject-build call "${{ matrix.vcvars64 }}" cmake -DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}" -DCMAKE_BUILD_TYPE=${{ matrix.cmake-build-type }} -DElastix_DIR=${{ github.workspace }}/Elastix-build -GNinja ../Elastix-source/dox/externalproject ninja shell: cmd - name: Run externalproject example if: ${{ !startsWith(matrix.os, 'windows') }} run: | externalproject-build/elastix_translation_example - name: Run externalproject example if: startsWith(matrix.os, 'windows') run: | set path=${{ github.workspace }}\Elastix-build\bin externalproject-build\elastix_translation_example.exe shell: cmd - name: Prepare Artifacts Unix if: ${{ !startsWith(matrix.os, 'windows') }} shell: bash run: | mkdir bin mkdir lib mkdir uploads mv Elastix-build/bin/elastix bin mv Elastix-build/bin/transformix bin mv Elastix-build/bin/${{ matrix.ANNLib }} lib mv Elastix-build/bin/${{ matrix.ANNLib2 }} lib mv Elastix-source/NOTICE uploads mv Elastix-source/LICENSE uploads mv bin uploads mv lib uploads - name: Prepare Artifacts Windows if: startsWith(matrix.os, 'windows') shell: bash run: | mkdir uploads mv Elastix-source/NOTICE uploads mv Elastix-source/LICENSE uploads mv Elastix-build/bin/elastix.exe uploads mv Elastix-build/bin/transformix.exe uploads mv Elastix-build/bin/${{ matrix.ANNLib }} uploads - name: Publish Artifacts uses: actions/upload-artifact@v3 with: name: "${{ matrix.os }}" path: uploads - name: Login to Docker Hub if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && startsWith(matrix.os, 'ubuntu') uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - name: Build and push if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && startsWith(matrix.os, 'ubuntu') run: | mkdir output docker build -t superelastix/elastix:${GITHUB_REF#refs/tags/} . docker run -u $UID:$GROUPS --mount type=bind,source="$(pwd)"/output,target=/out -v "$(pwd)"/Elastix-source/Testing/Data:/elastix/ superelastix/elastix:${GITHUB_REF#refs/tags/} elastix -out /out/ -p /elastix/parameters.3D.NC.euler.ASGD.001.txt -f /elastix/3DCT_lung_baseline.mha -m /elastix/3DCT_lung_followup.mha docker push superelastix/elastix:${GITHUB_REF#refs/tags/} elastix-5.2.0/.github/workflows/clang_format_check.yml000066400000000000000000000004721474534065100231140ustar00rootroot00000000000000name: test-clang-format on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: DoozyX/clang-format-lint-action@v0.11 with: source: '.' exclude: './CMake' extensions: 'hxx,h,cxx' clangFormatVersion: 8 style: file elastix-5.2.0/.gitignore000066400000000000000000000005641474534065100151750ustar00rootroot00000000000000# Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # Generated files and help/ Makefile CMakeFiles/ CTestTestfile.cmake .idea/ .githooks/clangFormatMac elastix-5.2.0/.gitmodules000066400000000000000000000001751474534065100153600ustar00rootroot00000000000000[submodule "dox/manual"] path = dox/manual url = https://github.com/SuperElastix/manual.git update = merge branch = main elastix-5.2.0/CMake/000077500000000000000000000000001474534065100141605ustar00rootroot00000000000000elastix-5.2.0/CMake/FindEigen3.cmake000066400000000000000000000053251474534065100171020ustar00rootroot00000000000000# - Try to find Eigen3 lib # # This module supports requiring a minimum version, e.g. you can do # find_package(Eigen3 3.1.2) # to require version 3.1.2 or newer of Eigen3. # # Once done this will define # # EIGEN3_FOUND - system has eigen lib with correct version # EIGEN3_INCLUDE_DIR - the eigen include directory # EIGEN3_VERSION - eigen version # Copyright (c) 2006, 2007 Montel Laurent, # Copyright (c) 2008, 2009 Gael Guennebaud, # Copyright (c) 2009 Benoit Jacob # Redistribution and use is allowed according to the terms of the 2-clause BSD license. if(NOT Eigen3_FIND_VERSION) if(NOT Eigen3_FIND_VERSION_MAJOR) set(Eigen3_FIND_VERSION_MAJOR 2) endif() if(NOT Eigen3_FIND_VERSION_MINOR) set(Eigen3_FIND_VERSION_MINOR 91) endif() if(NOT Eigen3_FIND_VERSION_PATCH) set(Eigen3_FIND_VERSION_PATCH 0) endif() set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") endif() macro(_eigen3_check_version) file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) set(EIGEN3_VERSION_OK FALSE) else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) set(EIGEN3_VERSION_OK TRUE) endif() if(NOT EIGEN3_VERSION_OK) message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " "but at least version ${Eigen3_FIND_VERSION} is required") endif() endmacro(_eigen3_check_version) if(EIGEN3_INCLUDE_DIR) # in cache already _eigen3_check_version() set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) else(EIGEN3_INCLUDE_DIR) find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library PATHS ${CMAKE_INSTALL_PREFIX}/include ${KDE4_INCLUDE_DIR} PATH_SUFFIXES eigen3 eigen ) if(EIGEN3_INCLUDE_DIR) _eigen3_check_version() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) mark_as_advanced(EIGEN3_INCLUDE_DIR) endif() elastix-5.2.0/CMake/FindOpenCL.cmake000066400000000000000000000635711474534065100171170ustar00rootroot00000000000000# - Try to find OpenCL # This module tries to find an OpenCL implementation on your system. # FindOpenCL.cmake supports following implementations of the OpenCL: # - Intel OpenCL SDK (Intel SDK for OpenCL) # - AMD OpenCL SDK (AMD APP SDK) # - NVidia OpenCL SDK (part of the NVIDIA CUDA Toolkit). # It also supports multiple OpenCL implementation and switching between them. # # By default FindOpenCL.cmake first tries to locate NVidia OpenCL SDK or AMD OpenCL SDK. # If they don't exist, search for Intel OpenCL SDK is performed. If system has multiple # OpenCL SDK installed (for example NVidia OpenCL SDK and Intel OpenCL SDK) FindOpenCL.cmake # will locate them and enable or disable the following flags: # OPENCL_INTEL_FOUND, OPENCL_NVIDIA_FOUND, OPENCL_AMD_FOUND (see documentation below). # User may manually control preferred OpenCL SDK by enabling or disabling following flags: # OPENCL_USE_INTEL_SDK, OPENCL_USE_NVIDIA_SDK, OPENCL_USE_AMD_SDK (see documentation below). # For Intel and AMD OpenCL SDK's switching between CPU and GPU implementations controls by: # OPENCL_USE_INTEL_SDK_GPU_CPU, OPENCL_USE_AMD_SDK_GPU_CPU (see documentation below). # In addition OpenCL profiling controls by OPENCL_PROFILING flag. # Various OpenCL math, optimization, warnings suppression and opencl c version # could be adjusted as well (see documentation below). # # Once done this will define: # OPENCL_FOUND - system has OpenCL. # OPENCL_INCLUDE_DIRS - the OpenCL include directory. Path to OpenCL header file 'CL/cl.h' # OPENCL_LIBRARIES - link these to use OpenCL. Path to OpenCL library 'OpenCL.lib' # # Other variables defined by this module listed below. # OPENCL_SDK_STRING - Hold selected OpenCL SDK. For example could be used for setting # project name of your project (see example below). # set(project_name_combined "myproject-${OPENCL_SDK_STRING}" CACHE INTERNAL "internal project name") # string(REPLACE " " "-" project_name_combined ${project_name_combined}) # set(myproject_project_name ${project_name_combined} CACHE string "project name" FORCE) # project(${myproject_project_name}) # # Intel OpenCL SDK variables: # OPENCL_INTEL_FOUND - true if Intel OpenCL SDK has been found on this system. # OPENCL_INTEL_INCLUDE_DIR - path to Intel OpenCL header file 'CL/cl.h'. # OPENCL_INTEL_LIBRARY - path to Intel OpenCL library 'OpenCL.lib'. # OPENCL_USE_INTEL_SDK - set Intel OpenCL SDK as selected. # OPENCL_USE_INTEL_SDK_GPU_CPU - use Intel GPU or CPU implementation of the OpenCL. # On use GPU, Off use CPU. # # NVidia OpenCL SDK variables: # OPENCL_NVIDIA_FOUND - true if NVidia OpenCL SDK has been found on this system. # OPENCL_NVIDIA_INCLUDE_DIR - path to NVidia OpenCL header file 'CL/cl.h'. # OPENCL_NVIDIA_LIBRARY - path to NVidia OpenCL library 'OpenCL.lib' # OPENCL_USE_NVIDIA_SDK - set NVidia OpenCL SDK as selected. # # AMD OpenCL SDK variables: # OPENCL_AMD_FOUND - true if AMD OpenCL SDK has been found on this system. # OPENCL_AMD_INCLUDE_DIR - path to AMD OpenCL header file 'CL/cl.h' # OPENCL_AMD_LIBRARY - path to AMD OpenCL library 'OpenCL.lib' # OPENCL_USE_AMD_SDK - set AMD OpenCL SDK as selected. # OPENCL_USE_AMD_SDK_GPU_CPU - use AMD GPU or CPU implementation of the OpenCL. # On use GPU, Off use CPU. # # OpenCL profiling options: # OPENCL_PROFILING - Enable OpenCL profiling with CL_QUEUE_PROFILING_ENABLE. # Event objects can be used to capture profiling information that measure execution time of a command. # # OpenCL math intrinsics options: # OPENCL_MATH_SINGLE_PRECISION_CONSTANT - Treat double precision floating-point constant # as single precision constant. # OPENCL_MATH_DENORMS_ARE_ZERO CACHE BOOL - This option controls how single precision and double # precision denormalized numbers are handled. # OPENCL_MATH_FP32_CORRECTLY_ROUNDED_DIVIDE_SQRT - This option allows an application to specify # that single precision floating-point divide (x/y and 1/x) and sqrt used # in the program source are correctly rounded. # # OpenCL optimization options: # OPENCL_OPTIMIZATION_OPT_DISABLE - This option disables all optimizations. # The default is optimizations are enabled. # OPENCL_OPTIMIZATION_MAD_ENABLE - Allow a * b + c to be replaced by a mad. # The mad computes a * b + c with reduced accuracy. # OPENCL_OPTIMIZATION_NO_SIGNED_ZEROS - Allow optimizations for floating-point arithmetic that # ignore the signedness of zero. # OPENCL_OPTIMIZATION_UNSAFE_MATH_OPTIMIZATIONS - Allow optimizations for floating-point arithmetic. # OPENCL_OPTIMIZATION_FINITE_MATH_ONLY - Allow optimizations for floating-point arithmetic that # assume that arguments and results are not NaNs or +-infinity. # OPENCL_OPTIMIZATION_FAST_RELAXED_MATH - Sets the optimization options # -cl-finite-math-only and -cl-unsafe-math-optimizations. # OPENCL_OPTIMIZATION_UNIFORM_WORK_GROUP_SIZE - This requires that the global work-size be a # multiple of the work-group size specified to clEnqueueNDRangeKernel. # # OpenCL options to request or suppress warnings: # OPENCL_WARNINGS_DISABLE - This option inhibit all warning messages. # OPENCL_WARNINGS_AS_ERRORS - This option make all warnings into errors. # # OpenCL options controlling the opencl c version: # OPENCL_C_VERSION_1_1 - This option determine the OpenCL C language version to use. # Support all OpenCL C programs that use the OpenCL C language 1.1 specification. # OPENCL_C_VERSION_1_2 - This option determine the OpenCL C language version to use. # Support all OpenCL C programs that use the OpenCL C language 1.2 specification. # OPENCL_C_VERSION_2_0 - This option determine the OpenCL C language version to use. # Support all OpenCL C programs that use the OpenCL C language 2.0 specification. # #============================================================================= # \author Denis P. Shamonin and Marius Staring. Division of Image Processing, # Department of Radiology, Leiden, The Netherlands # # \note This work was funded by the Netherlands Organisation for # Scientific Research (NWO NRG-2010.02 and NWO 639.021.124). #============================================================================= # (To distribute this file outside of elastix, substitute the full author # text for the above reference.) find_package(PackageHandleStandardArgs) #============================================================================= # Macros for FindOpenCL.cmake # macro to set FindOpenCL.cmake initial state macro(opencl_init) # Define variable OPENCL_DEFAULT_SDK_SELECTED if(NOT DEFINED OPENCL_DEFAULT_SDK_SELECTED) set(OPENCL_DEFAULT_SDK_SELECTED FALSE CACHE INTERNAL "OpenCL SDK has been selected by default.") endif() # Define OPENCL_INCLUDE_DIRS if(NOT DEFINED OPENCL_INCLUDE_DIRS) set(OPENCL_INCLUDE_DIRS OPENCL_INCLUDE_DIRS-NOTFOUND CACHE PATH "Path to OpenCL header file 'CL/cl.h'.") else() if(NOT OPENCL_INCLUDE_DIRS MATCHES NOTFOUND) if(NOT EXISTS "${OPENCL_INCLUDE_DIRS}/CL/cl.h") message(WARNING "OpenCL header 'cl.h' has not been found at '${OPENCL_INCLUDE_DIRS}/CL'") set(OPENCL_INCLUDE_DIRS OPENCL_INCLUDE_DIRS-NOTFOUND CACHE PATH "Path to OpenCL header file 'CL/cl.h'." FORCE) endif() endif() endif() # Define OPENCL_LIBRARIES if(NOT DEFINED OPENCL_LIBRARIES) set(OPENCL_LIBRARIES OPENCL_LIBRARIES-NOTFOUND CACHE PATH "Path to OpenCL library 'OpenCL.lib'.") else() if(NOT OPENCL_LIBRARIES MATCHES NOTFOUND) if(NOT EXISTS ${OPENCL_LIBRARIES}) message(WARNING "OpenCL library 'OpenCL.lib' has not been found at '${OPENCL_LIBRARIES}'") set(OPENCL_LIBRARIES OPENCL_LIBRARIES-NOTFOUND CACHE PATH "Path to OpenCL library 'OpenCL.lib'." FORCE) endif() endif() endif() # Define OpenCL header name if(WIN32 OR UNIX) set(OPENCL_HEADER_NAME "CL/cl.h" CACHE INTERNAL "OpenCL header name CL/cl.h (Win and Unix) and OpenCL/cl.h (Apple).") elseif(APPLE) set(OPENCL_HEADER_NAME "OpenCL/cl.h" CACHE INTERNAL "OpenCL header name CL/cl.h (Win and Unix) and OpenCL/cl.h (Apple).") endif() endmacro() # macro to find Intel OpenCL SDK macro(opencl_find_intel) # Set find result to false set(OPENCL_INTEL_FOUND FALSE CACHE INTERNAL "Intel OpenCL SDK has not been found on this system.") # Find cl.h in Intel OpenCL SDK find_path(OPENCL_INTEL_INCLUDE_DIR ${OPENCL_HEADER_NAME} HINTS $ENV{INTELOCLSDKROOT} PATH_SUFFIXES include PATHS /usr NO_DEFAULT_PATH ) # Make it internal set(OPENCL_INTEL_INCLUDE_DIR ${OPENCL_INTEL_INCLUDE_DIR} CACHE INTERNAL "Intel OpenCL include directory.") if(CMAKE_SIZEOF_VOID_P EQUAL 8) find_library(OPENCL_INTEL_LIBRARY NAMES OpenCL HINTS $ENV{INTELOCLSDKROOT} PATH_SUFFIXES lib/x64 PATHS /usr/lib NO_DEFAULT_PATH ) else() find_library(OPENCL_INTEL_LIBRARY NAMES OpenCL HINTS $ENV{INTELOCLSDKROOT} PATH_SUFFIXES lib/x86 PATHS /usr/lib NO_DEFAULT_PATH ) endif() # Make it internal set(OPENCL_INTEL_LIBRARY ${OPENCL_INTEL_LIBRARY} CACHE INTERNAL "Intel OpenCL library.") if(NOT (${OPENCL_INTEL_INCLUDE_DIR} STREQUAL OPENCL_INTEL_INCLUDE_DIR-NOTFOUND) AND NOT (${OPENCL_INTEL_LIBRARY} STREQUAL OPENCL_INTEL_LIBRARY-NOTFOUND)) set(OPENCL_INTEL_FOUND TRUE CACHE INTERNAL "Intel OpenCL SDK has not been found on this system.") endif() endmacro() # macro to find NVidia OpenCL SDK macro(opencl_find_nvidia) # Set find result to false set(OPENCL_NVIDIA_FOUND FALSE CACHE INTERNAL "NVidia OpenCL SDK has not been found on this system.") # Find cl.h in NVidia OpenCL SDK find_path(OPENCL_NVIDIA_INCLUDE_DIR ${OPENCL_HEADER_NAME} HINTS $ENV{CUDA_PATH} PATH_SUFFIXES include PATHS /usr/local/cuda NO_DEFAULT_PATH ) # Make it internal set(OPENCL_NVIDIA_INCLUDE_DIR ${OPENCL_NVIDIA_INCLUDE_DIR} CACHE INTERNAL "NVidia OpenCL include directory.") if(CMAKE_SIZEOF_VOID_P EQUAL 8) find_library(OPENCL_NVIDIA_LIBRARY NAMES OpenCL HINTS $ENV{CUDA_PATH} PATH_SUFFIXES lib/x64 PATHS /usr/local/cuda/lib64 NO_DEFAULT_PATH ) else() find_library(OPENCL_NVIDIA_LIBRARY NAMES OpenCL HINTS $ENV{CUDA_PATH} PATH_SUFFIXES lib/Win32 PATHS /usr/local/cuda/lib NO_DEFAULT_PATH ) endif() # Make it internal set(OPENCL_NVIDIA_LIBRARY ${OPENCL_NVIDIA_LIBRARY} CACHE INTERNAL "NVidia OpenCL library.") if(NOT (${OPENCL_NVIDIA_INCLUDE_DIR} STREQUAL OPENCL_NVIDIA_INCLUDE_DIR-NOTFOUND) AND NOT (${OPENCL_NVIDIA_LIBRARY} STREQUAL OPENCL_NVIDIA_LIBRARY-NOTFOUND)) set(OPENCL_NVIDIA_FOUND TRUE CACHE INTERNAL "NVidia OpenCL SDK has been found on this system.") endif() endmacro() # macro to find AMD OpenCL SDK macro(opencl_find_amd) # Set find result to false set(OPENCL_AMD_FOUND FALSE CACHE INTERNAL "AMD OpenCL SDK has not been found on this system.") # Find cl.h in AMD OpenCL SDK find_path(OPENCL_AMD_INCLUDE_DIR ${OPENCL_HEADER_NAME} HINTS $ENV{AMDAPPSDKROOT} PATH_SUFFIXES include PATHS /opt/AMDAPP NO_DEFAULT_PATH ) # Make it internal set(OPENCL_AMD_INCLUDE_DIR ${OPENCL_AMD_INCLUDE_DIR} CACHE INTERNAL "AMD OpenCL include directory.") if(CMAKE_SIZEOF_VOID_P EQUAL 8) find_library(OPENCL_AMD_LIBRARY NAMES OpenCL HINTS $ENV{AMDAPPSDKROOT} PATH_SUFFIXES lib/x86_64 PATHS /opt/AMDAPP/lib NO_DEFAULT_PATH ) else() find_library(OPENCL_AMD_LIBRARY NAMES OpenCL HINTS $ENV{AMDAPPSDKROOT} PATH_SUFFIXES lib/x86 PATHS /opt/AMDAPP/lib NO_DEFAULT_PATH ) endif() # Make it internal set(OPENCL_AMD_LIBRARY ${OPENCL_AMD_LIBRARY} CACHE INTERNAL "AMD OpenCL library.") if(NOT (${OPENCL_AMD_INCLUDE_DIR} STREQUAL OPENCL_AMD_INCLUDE_DIR-NOTFOUND) AND NOT (${OPENCL_AMD_LIBRARY} STREQUAL OPENCL_AMD_LIBRARY-NOTFOUND)) set(OPENCL_AMD_FOUND TRUE CACHE INTERNAL "AMD OpenCL SDK has not been found on this system.") endif() endmacro() # macro opencl_define_avaliable_sdk macro(opencl_define_avaliable_sdk) # Define OPENCL_USE_INTEL_SDK if(NOT DEFINED OPENCL_USE_INTEL_SDK AND OPENCL_INTEL_FOUND) if(NOT OPENCL_USE_NVIDIA_SDK AND NOT OPENCL_USE_AMD_SDK) set(OPENCL_USE_INTEL_SDK FALSE CACHE BOOL "Use Intel implementation of the OpenCL.") set(OPENCL_USE_INTEL_SDK_GPU_CPU TRUE CACHE BOOL "Use Intel GPU or CPU implementation of the OpenCL. On use GPU, Off use CPU.") endif() elseif(DEFINED OPENCL_USE_INTEL_SDK) if(OPENCL_USE_NVIDIA_SDK OR OPENCL_USE_AMD_SDK) unset(OPENCL_USE_INTEL_SDK CACHE) unset(OPENCL_USE_INTEL_SDK_GPU_CPU CACHE) endif() endif() # Define OPENCL_USE_NVIDIA_SDK if(NOT DEFINED OPENCL_USE_NVIDIA_SDK AND OPENCL_NVIDIA_FOUND) if(NOT OPENCL_USE_INTEL_SDK AND NOT OPENCL_USE_AMD_SDK) set(OPENCL_USE_NVIDIA_SDK FALSE CACHE BOOL "Use NVidia implementation of the OpenCL.") endif() elseif(DEFINED OPENCL_USE_NVIDIA_SDK) if(OPENCL_USE_INTEL_SDK OR OPENCL_USE_AMD_SDK) unset(OPENCL_USE_NVIDIA_SDK CACHE) endif() endif() # Define OPENCL_USE_AMD_SDK if(NOT DEFINED OPENCL_USE_AMD_SDK AND OPENCL_AMD_FOUND) if(NOT OPENCL_USE_INTEL_SDK AND NOT OPENCL_USE_NVIDIA_SDK) set(OPENCL_USE_AMD_SDK FALSE CACHE BOOL "Use AMD implementation of the OpenCL.") set(OPENCL_USE_AMD_SDK_GPU_CPU TRUE CACHE BOOL "Use AMD GPU or CPU implementation of the OpenCL. On use GPU, Off use CPU.") endif() elseif(DEFINED OPENCL_USE_AMD_SDK) if(OPENCL_USE_INTEL_SDK OR OPENCL_USE_NVIDIA_SDK) unset(OPENCL_USE_AMD_SDK CACHE) unset(OPENCL_USE_AMD_SDK_GPU_CPU CACHE) endif() endif() endmacro() # macro opencl_select_sdk macro(opencl_select_sdk) if(OPENCL_USE_INTEL_SDK) set(OPENCL_INCLUDE_DIRS ${OPENCL_INTEL_INCLUDE_DIR} CACHE PATH "Path to Intel OpenCL header file 'CL/cl.h'." FORCE) set(OPENCL_LIBRARIES ${OPENCL_INTEL_LIBRARY} CACHE PATH "Path to Intel OpenCL library 'OpenCL.lib'." FORCE) # Set CMAKE_CXX_FLAGS if(OPENCL_USE_INTEL_SDK_GPU_CPU) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_USE_INTEL_GPU") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_USE_INTEL_CPU") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_USE_INTEL") set(OPENCL_SDK_STRING "Intel OpenCL" CACHE INTERNAL "Selected OpenCL SDK") elseif(OPENCL_USE_NVIDIA_SDK) set(OPENCL_INCLUDE_DIRS ${OPENCL_NVIDIA_INCLUDE_DIR} CACHE PATH "Path to NVidia OpenCL header file 'CL/cl.h'." FORCE) set(OPENCL_LIBRARIES ${OPENCL_NVIDIA_LIBRARY} CACHE PATH "Path to NVidia OpenCL library 'OpenCL.lib'." FORCE) # Set CMAKE_CXX_FLAGS set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_USE_NVIDIA") set(OPENCL_SDK_STRING "NVidia OpenCL" CACHE INTERNAL "Selected OpenCL SDK") elseif(OPENCL_USE_AMD_SDK) set(OPENCL_INCLUDE_DIRS ${OPENCL_AMD_INCLUDE_DIR} CACHE PATH "Path to AMD OpenCL header file 'CL/cl.h'." FORCE) set(OPENCL_LIBRARIES ${OPENCL_AMD_LIBRARY} CACHE PATH "Path to AMD OpenCL library 'OpenCL.lib'." FORCE) # Set CMAKE_CXX_FLAGS if(OPENCL_USE_AMD_SDK_GPU_CPU) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_USE_AMD_GPU") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_USE_AMD_CPU") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_USE_AMD -DATI_OS_WIN") set(OPENCL_SDK_STRING "AMD OpenCL" CACHE INTERNAL "Selected OpenCL SDK") else() set(OPENCL_SDK_STRING "OpenCL not found" CACHE INTERNAL "Selected OpenCL SDK") #message(WARNING "Please select OpenCL platform") endif() endmacro() # macro opencl_select_default_sdk macro(opencl_select_default_sdk) if(NOT OPENCL_DEFAULT_SDK_SELECTED AND NOT OPENCL_USE_INTEL_SDK AND NOT OPENCL_USE_NVIDIA_SDK AND NOT OPENCL_USE_AMD_SDK) # If both NVidia SDK and AMD SDK exist on the system, # then let user to specify which platform to use if(OPENCL_NVIDIA_FOUND AND OPENCL_AMD_FOUND) message(SEND_ERROR "Both NVidia OpenCL SDK and AMD OpenCL SDK exists on this system. Please select the OpenCL platform.") # If NVidia SDK has been found without AMD SDK elseif(OPENCL_NVIDIA_FOUND AND NOT OPENCL_AMD_FOUND) set(OPENCL_USE_NVIDIA_SDK TRUE CACHE BOOL "Use NVidia implementation of the OpenCL." FORCE) opencl_select_sdk() # If AMD SDK has been found without NVidia SDK elseif(OPENCL_AMD_FOUND AND NOT OPENCL_NVIDIA_FOUND) set(OPENCL_USE_AMD_SDK TRUE CACHE BOOL "Use AMD implementation of the OpenCL." FORCE) opencl_select_sdk() # If only Intel SDK has been found without NVidia SDK or AMD SDK elseif(OPENCL_INTEL_FOUND AND (NOT OPENCL_NVIDIA_FOUND OR NOT OPENCL_AMD_FOUND)) set(OPENCL_USE_INTEL_SDK TRUE CACHE BOOL "Use Intel implementation of the OpenCL." FORCE) opencl_select_sdk() endif() # Set the selected flag set(OPENCL_DEFAULT_SDK_SELECTED TRUE CACHE INTERNAL "OpenCL SDK has been selected by default.") endif() endmacro() # macro opencl_define_options macro(opencl_define_options) if(OPENCL_USE_INTEL_SDK OR OPENCL_USE_NVIDIA_SDK OR OPENCL_USE_AMD_SDK) # Define profiling variable for ITK4OpenCL set(OPENCL_PROFILING OFF CACHE BOOL "Enable OpenCL profiling with CL_QUEUE_PROFILING_ENABLE. Event objects can be used to capture profiling information that measure execution time of a command.") mark_as_advanced(OPENCL_PROFILING) # OpenCL Math Intrinsics Options set(OPENCL_MATH_SINGLE_PRECISION_CONSTANT CACHE BOOL "Treat double precision floating-point constant as single precision constant.") set(OPENCL_MATH_DENORMS_ARE_ZERO CACHE BOOL "This option controls how single precision and double precision denormalized numbers are handled.") set(OPENCL_MATH_FP32_CORRECTLY_ROUNDED_DIVIDE_SQRT CACHE BOOL "This option allows an application to specify that single precision floating-point divide (x/y and 1/x) and sqrt used in the program source are correctly rounded.") mark_as_advanced(OPENCL_MATH_SINGLE_PRECISION_CONSTANT) mark_as_advanced(OPENCL_MATH_DENORMS_ARE_ZERO) mark_as_advanced(OPENCL_MATH_FP32_CORRECTLY_ROUNDED_DIVIDE_SQRT) # OpenCL Optimization Options set(OPENCL_OPTIMIZATION_OPT_DISABLE CACHE BOOL "This option disables all optimizations. The default is optimizations are enabled.") set(OPENCL_OPTIMIZATION_MAD_ENABLE CACHE BOOL "Allow a * b + c to be replaced by a mad. The mad computes a * b + c with reduced accuracy.") set(OPENCL_OPTIMIZATION_NO_SIGNED_ZEROS CACHE BOOL "Allow optimizations for floating-point arithmetic that ignore the signedness of zero.") set(OPENCL_OPTIMIZATION_UNSAFE_MATH_OPTIMIZATIONS CACHE BOOL "Allow optimizations for floating-point arithmetic.") set(OPENCL_OPTIMIZATION_FINITE_MATH_ONLY CACHE BOOL "Allow optimizations for floating-point arithmetic that assume that arguments and results are not NaNs or +-infinity.") set(OPENCL_OPTIMIZATION_FAST_RELAXED_MATH CACHE BOOL "Sets the optimization options -cl-finite-math-only and -cl-unsafe-math-optimizations.") set(OPENCL_OPTIMIZATION_UNIFORM_WORK_GROUP_SIZE CACHE BOOL "This requires that the global work-size be a multiple of the work-group size specified to clEnqueueNDRangeKernel.") mark_as_advanced(OPENCL_OPTIMIZATION_OPT_DISABLE) mark_as_advanced(OPENCL_OPTIMIZATION_MAD_ENABLE) mark_as_advanced(OPENCL_OPTIMIZATION_NO_SIGNED_ZEROS) mark_as_advanced(OPENCL_OPTIMIZATION_UNSAFE_MATH_OPTIMIZATIONS) mark_as_advanced(OPENCL_OPTIMIZATION_FINITE_MATH_ONLY) mark_as_advanced(OPENCL_OPTIMIZATION_FAST_RELAXED_MATH) mark_as_advanced(OPENCL_OPTIMIZATION_UNIFORM_WORK_GROUP_SIZE) # OpenCL Options to Request or Suppress Warnings set(OPENCL_WARNINGS_DISABLE CACHE BOOL "This option inhibit all warning messages.") set(OPENCL_WARNINGS_AS_ERRORS CACHE BOOL "This option make all warnings into errors.") mark_as_advanced(OPENCL_WARNINGS_DISABLE) mark_as_advanced(OPENCL_WARNINGS_AS_ERRORS) # OpenCL Options Controlling the OpenCL C Version set(OPENCL_C_VERSION_1_1 CACHE BOOL "This option determine the OpenCL C language version to use. Support all OpenCL C programs that use the OpenCL C language 1.1 specification.") set(OPENCL_C_VERSION_1_2 CACHE BOOL "This option determine the OpenCL C language version to use. Support all OpenCL C programs that use the OpenCL C language 1.2 specification.") set(OPENCL_C_VERSION_2_0 CACHE BOOL "This option determine the OpenCL C language version to use. Support all OpenCL C programs that use the OpenCL C language 2.0 specification.") mark_as_advanced(OPENCL_C_VERSION_1_1) mark_as_advanced(OPENCL_C_VERSION_1_2) mark_as_advanced(OPENCL_C_VERSION_2_0) else() unset(OPENCL_PROFILING CACHE) unset(OPENCL_MATH_SINGLE_PRECISION_CONSTANT CACHE) unset(OPENCL_MATH_DENORMS_ARE_ZERO CACHE) unset(OPENCL_MATH_FP32_CORRECTLY_ROUNDED_DIVIDE_SQRT CACHE) unset(OPENCL_OPTIMIZATION_OPT_DISABLE CACHE) unset(OPENCL_OPTIMIZATION_MAD_ENABLE CACHE) unset(OPENCL_OPTIMIZATION_NO_SIGNED_ZEROS CACHE) unset(OPENCL_OPTIMIZATION_UNSAFE_MATH_OPTIMIZATIONS CACHE) unset(OPENCL_OPTIMIZATION_FINITE_MATH_ONLY CACHE) unset(OPENCL_OPTIMIZATION_FAST_RELAXED_MATH CACHE) unset(OPENCL_OPTIMIZATION_UNIFORM_WORK_GROUP_SIZE CACHE) unset(OPENCL_WARNINGS_DISABLE CACHE) unset(OPENCL_WARNINGS_AS_ERRORS CACHE) unset(OPENCL_C_VERSION_1_1 CACHE) unset(OPENCL_C_VERSION_1_2 CACHE) unset(OPENCL_C_VERSION_2_0 CACHE) endif() endmacro() # macro opencl_append_options_to_cxx_flags macro(opencl_append_options_to_cxx_flags) # Add extra options for AMD if(OPENCL_USE_AMD_SDK) # Set OPENCL_MATH_SINGLE_PRECISION_CONSTANT obligingly for AMD set(OPENCL_MATH_SINGLE_PRECISION_CONSTANT ON CACHE BOOL "Treat double precision floating-point constant as single precision constant." FORCE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_MATH_SINGLE_PRECISION_CONSTANT") else() if(OPENCL_MATH_SINGLE_PRECISION_CONSTANT) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_MATH_SINGLE_PRECISION_CONSTANT") endif() endif() if(OPENCL_MATH_DENORMS_ARE_ZERO) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_MATH_DENORMS_ARE_ZERO") endif() if(OPENCL_MATH_FP32_CORRECTLY_ROUNDED_DIVIDE_SQRT) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_MATH_FP32_CORRECTLY_ROUNDED_DIVIDE_SQRT") endif() # Add OpenCL Optimization if(OPENCL_OPTIMIZATION_OPT_DISABLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_OPTIMIZATION_OPT_DISABLE") unset(OPENCL_OPTIMIZATION_MAD_ENABLE CACHE) unset(OPENCL_OPTIMIZATION_NO_SIGNED_ZEROS CACHE) unset(OPENCL_OPTIMIZATION_UNSAFE_MATH_OPTIMIZATIONS CACHE) unset(OPENCL_OPTIMIZATION_FINITE_MATH_ONLY CACHE) unset(OPENCL_OPTIMIZATION_FAST_RELAXED_MATH CACHE) unset(OPENCL_OPTIMIZATION_UNIFORM_WORK_GROUP_SIZE CACHE) endif() if(NOT OPENCL_OPTIMIZATION_OPT_DISABLE) if(OPENCL_OPTIMIZATION_MAD_ENABLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_OPTIMIZATION_MAD_ENABLE") endif() if(OPENCL_OPTIMIZATION_NO_SIGNED_ZEROS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_OPTIMIZATION_NO_SIGNED_ZEROS") endif() if(OPENCL_OPTIMIZATION_UNIFORM_WORK_GROUP_SIZE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_OPTIMIZATION_UNIFORM_WORK_GROUP_SIZE") endif() if(OPENCL_OPTIMIZATION_FAST_RELAXED_MATH) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_OPTIMIZATION_FAST_RELAXED_MATH") unset(OPENCL_OPTIMIZATION_UNSAFE_MATH_OPTIMIZATIONS CACHE) unset(OPENCL_OPTIMIZATION_FINITE_MATH_ONLY CACHE) endif() if(NOT OPENCL_OPTIMIZATION_FAST_RELAXED_MATH) if(OPENCL_OPTIMIZATION_UNSAFE_MATH_OPTIMIZATIONS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_OPTIMIZATION_UNSAFE_MATH_OPTIMIZATIONS") endif() if(OPENCL_OPTIMIZATION_FINITE_MATH_ONLY) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_OPTIMIZATION_FINITE_MATH_ONLY") endif() endif() endif() # Add OpenCL Warnings if(OPENCL_WARNINGS_DISABLE) unset(OPENCL_WARNINGS_AS_ERRORS CACHE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_WARNINGS_DISABLE") endif() if(OPENCL_WARNINGS_AS_ERRORS) unset(OPENCL_WARNINGS_DISABLE CACHE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_WARNINGS_AS_ERRORS") endif() # Add Options Controlling the OpenCL C Version if(OPENCL_C_VERSION_1_1) unset(OPENCL_C_VERSION_1_2 CACHE) unset(OPENCL_C_VERSION_2_0 CACHE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_C_VERSION_1_1") endif() if(OPENCL_C_VERSION_1_2) unset(OPENCL_C_VERSION_1_1 CACHE) unset(OPENCL_C_VERSION_2_0 CACHE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_C_VERSION_1_2") endif() if(OPENCL_C_VERSION_2_0) unset(OPENCL_C_VERSION_1_0 CACHE) unset(OPENCL_C_VERSION_1_1 CACHE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_C_VERSION_2_0") endif() # Add OpenCL Profiling if(OPENCL_PROFILING) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPENCL_PROFILING") endif() endmacro() #============================================================================= # FindOpenCL.cmake starts here opencl_init() # Set FindOpenCL.cmake to initial state opencl_find_intel() # Find Intel OpenCL SDK opencl_find_nvidia() # Find NVIDIA OpenCL SDK opencl_find_amd() # Find AMD OpenCL SDK opencl_define_avaliable_sdk() # Define OpenCL SDK's opencl_select_sdk() # Select OpenCL SDK opencl_select_default_sdk() # Perform selecting default OpenCL SDK opencl_define_options() # Define OpenCL options opencl_append_options_to_cxx_flags() # Append OpenCL options to CMAKE_CXX_FLAGS # handle the QUIETLY and REQUIRED arguments and set OpenCL_FOUND to TRUE if # all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS( OpenCL DEFAULT_MSG OPENCL_LIBRARIES OPENCL_INCLUDE_DIRS) mark_as_advanced(OPENCL_INCLUDE_DIRS) mark_as_advanced(OPENCL_LIBRARIES) elastix-5.2.0/CMake/elastixExportTarget.cmake000066400000000000000000000051761474534065100212150ustar00rootroot00000000000000function(elastix_export_target tgt) # Remove the build tree's ElastixTargets file if this is the first call: get_property(first_time GLOBAL PROPERTY ELASTIX_FIRST_EXPORTED_TARGET) if(NOT first_time) file(REMOVE ${elastix_BINARY_DIR}/ElastixTargets.cmake) set_property(GLOBAL PROPERTY ELASTIX_FIRST_EXPORTED_TARGET 1) endif() get_target_property(type ${tgt} TYPE) if (type STREQUAL "STATIC_LIBRARY" OR type STREQUAL "MODULE_LIBRARY" OR type STREQUAL "SHARED_LIBRARY") set_property(TARGET ${tgt} PROPERTY VERSION 1) set_property(TARGET ${tgt} PROPERTY SOVERSION 1) if ("${tgt}" STREQUAL "elastix_lib") set_property(TARGET ${tgt} PROPERTY OUTPUT_NAME elastix-${ELASTIX_VERSION_MAJOR}.${ELASTIX_VERSION_MINOR}) elseif ("${tgt}" STREQUAL "transformix_lib") set_property(TARGET ${tgt} PROPERTY OUTPUT_NAME transformix-${ELASTIX_VERSION_MAJOR}.${ELASTIX_VERSION_MINOR}) else() set_property(TARGET ${tgt} PROPERTY OUTPUT_NAME ${tgt}-${ELASTIX_VERSION_MAJOR}.${ELASTIX_VERSION_MINOR}) endif() if(type STREQUAL "STATIC_LIBRARY") if(NOT ELASTIX_NO_INSTALL_DEVELOPMENT) install(TARGETS ${tgt} EXPORT ElastixTargets RUNTIME DESTINATION ${ELASTIX_INSTALL_RUNTIME_DIR} LIBRARY DESTINATION ${ELASTIX_INSTALL_LIBRARY_DIR} ARCHIVE DESTINATION ${ELASTIX_INSTALL_ARCHIVE_DIR} COMPONENT Development ) endif() else() if(NOT ELASTIX_NO_INSTALL_RUNTIME_LIBRARIES) install(TARGETS ${tgt} EXPORT ElastixTargets RUNTIME DESTINATION ${ELASTIX_INSTALL_RUNTIME_DIR} LIBRARY DESTINATION ${ELASTIX_INSTALL_LIBRARY_DIR} ARCHIVE DESTINATION ${ELASTIX_INSTALL_ARCHIVE_DIR} COMPONENT RuntimeLibraries ) endif() endif() elseif(type STREQUAL "EXECUTABLE") if(NOT ELASTIX_NO_INSTALL_RUNTIME_LIBRARIES) install(TARGETS ${tgt} EXPORT ElastixTargets RUNTIME DESTINATION ${ELASTIX_INSTALL_RUNTIME_DIR} LIBRARY DESTINATION ${ELASTIX_INSTALL_LIBRARY_DIR} ARCHIVE DESTINATION ${ELASTIX_INSTALL_ARCHIVE_DIR} COMPONENT Executables ) endif() else() if(NOT ELASTIX_NO_INSTALL_DEVELOPMENT) install(TARGETS ${tgt} EXPORT ElastixTargets RUNTIME DESTINATION ${ELASTIX_INSTALL_RUNTIME_DIR} LIBRARY DESTINATION ${ELASTIX_INSTALL_LIBRARY_DIR} ARCHIVE DESTINATION ${ELASTIX_INSTALL_ARCHIVE_DIR} COMPONENT Development ) endif() endif() export(TARGETS ${tgt} APPEND FILE "${elastix_BINARY_DIR}/ElastixTargets.cmake" ) endfunction() elastix-5.2.0/CMake/elastixOpenCL.cmake000066400000000000000000000216251474534065100177020ustar00rootroot00000000000000# # \note This file is a copy of the ITK 4.2.0 CMake\itkOpenCL.cmake # We added support for .clh file(OpenCL header kernels) # It was modified by Denis P. Shamonin and Marius Staring. # Division of Image Processing, # Department of Radiology, Leiden, The Netherlands. # Added functionality is described in the Insight Journal paper: # http://hdl.handle.net/10380/3393 # #----------------------------------------------------------------------------- # OpenCL interface macros. # opencl_source_file_to_string() # macro(opencl_source_file_to_string _source_file _result_cmake_var) file(STRINGS ${_source_file} FileStrings) foreach(SourceLine ${FileStrings}) # replace all \ with \\ to make the c string constant work string(REGEX REPLACE "\\\\" "\\\\\\\\" TempSourceLine "${SourceLine}") # replace all " with \" to make the c string constant work string(REGEX REPLACE "\"" "\\\\\"" EscapedSourceLine "${TempSourceLine}") set(${_result_cmake_var} "${${_result_cmake_var}}\n\"${EscapedSourceLine}\\n\"") endforeach() endmacro() #----------------------------------------------------------------------------- # OpenCL interface macros. # write_opencl_kernel_to_file() # macro(write_opencl_kernel_to_file _opencl_file _src_var _group_name) #message(STATUS "write_opencl_kernel_to_file macro variables") #message(STATUS "VAR _opencl_file: ${_opencl_file}") #message(STATUS "VAR _src_var: ${_src_var}") #message(STATUS "VAR _group_name: ${_group_name}") # get file name and extension get_filename_component(OpenCLFileName ${_opencl_file} NAME_WE) get_filename_component(OpenCLFileExtension ${_opencl_file} EXT) # define kernel class, output filename for generated file and opencl file copy set(kernel_cxx_include "itk${OpenCLFileName}.h") set(kernel_cxx_class_name) set(output_file_generated) set(opencl_file_copy) if(${OpenCLFileExtension} STREQUAL ".clh") set(kernel_cxx_class_name ${OpenCLFileName}HeaderKernel) set(output_file_generated "${OpenCLFileName}HeaderKernel.cxx") set(opencl_file_copy "${output_file_generated}.clh") elseif(${OpenCLFileExtension} STREQUAL ".cl") set(kernel_cxx_class_name ${OpenCLFileName}Kernel) set(output_file_generated "${OpenCLFileName}Kernel.cxx") set(opencl_file_copy "${output_file_generated}.cl") endif() #message(STATUS "kernel_cxx_include : ${kernel_cxx_include}") #message(STATUS "kernel_cxx_class_name: ${kernel_cxx_class_name}") #message(STATUS "output_file_generated: ${output_file_generated}") #message(STATUS "opencl_file_copy : ${opencl_file_copy}") # convert file to string opencl_source_file_to_string(${_opencl_file} ${kernel_cxx_class_name}_SourceString) # add include on top set(${kernel_cxx_class_name}_KernelString "#include \"${kernel_cxx_include}\"\n\n") # add namespace itk set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString}namespace itk\n") # add const char* ${_kernel_cxx_class_name}::GetOpenCLSource() here set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString}{\n\n") set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString}const char* ${kernel_cxx_class_name}::GetOpenCLSource()\n") set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString}{\n") # add converted string set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString} return ${${kernel_cxx_class_name}_SourceString};\n") set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString}}\n\n") # add closing bracket to namespace itk set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString}} // namespace itk\n") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${output_file_generated} "${${kernel_cxx_class_name}_KernelString}") configure_file(${_opencl_file} ${CMAKE_CURRENT_BINARY_DIR}/${opencl_file_copy} COPYONLY) add_custom_target(${kernel_cxx_class_name}_Target DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${output_file_generated} ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt ${CMAKE_CURRENT_BINARY_DIR}/${opencl_file_copy} ) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${output_file_generated} PROPERTIES GENERATED ON) set(${_src_var} ${${_src_var}} ${output_file_generated}) set_property(TARGET ${kernel_cxx_class_name}_Target PROPERTY FOLDER ${_group_name}) endmacro() #----------------------------------------------------------------------------- # OpenCL interface macros. # write_opencl_kernels_to_file() # macro(write_opencl_kernels_to_file _opencl_files _merge_to _src_var _group_name) #message(STATUS "write_opencl_kernels_to_file macro variables") #message(STATUS "VAR _opencl_files: ${_opencl_files}") #message(STATUS "VAR _merge_to: ${_merge_to}") #message(STATUS "VAR _src_var: ${_src_var}") #message(STATUS "VAR _group_name: ${_group_name}") set(kernel_cxx_class_name ${_merge_to}Kernel) #message(STATUS "kernel_cxx_class_name: ${kernel_cxx_class_name}") set(output_file_generated "${kernel_cxx_class_name}.cxx") # add include on top set(${kernel_cxx_class_name}_KernelString "#include \"itk${_merge_to}.h\"\n\n") # add namespace itk set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString}namespace itk\n{\n") foreach(opencl_file ${_opencl_files}) # get file name and extension get_filename_component(OpenCLFileName ${opencl_file} NAME_WE) get_filename_component(OpenCLFileExtension ${opencl_file} EXT) # get the file name get_filename_component(OpenCLFileName ${opencl_file} NAME_WE) # convert file to string opencl_source_file_to_string(${opencl_file} ${OpenCLFileName}_SourceString) # add const char* ${kernel_cxx_class_name}::GetOpenCLSource() here set(${OpenCLFileName}_KernelString "${${OpenCLFileName}_KernelString}const char* ${OpenCLFileName}Kernel::GetOpenCLSource()\n") set(${OpenCLFileName}_KernelString "${${OpenCLFileName}_KernelString}{\n") # add converted string set(${OpenCLFileName}_KernelString "${${OpenCLFileName}_KernelString} return ${${OpenCLFileName}_SourceString};\n") set(${OpenCLFileName}_KernelString "${${OpenCLFileName}_KernelString}}\n\n") # append to ${kernel_cxx_class_name}_KernelString set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString} ${${OpenCLFileName}_KernelString}") endforeach() # add closing bracket to namespace itk set(${kernel_cxx_class_name}_KernelString "${${kernel_cxx_class_name}_KernelString}} // namespace itk\n") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${output_file_generated} "${${kernel_cxx_class_name}_KernelString}") # make sure that if we modify original OpenCL file from ${_opencl_files}, then kernel string has to be recreated set(opencl_depend_files "") list(APPEND opencl_depend_files "${CMAKE_CURRENT_BINARY_DIR}/${output_file_generated}") list(APPEND opencl_depend_files "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt") foreach(opencl_file ${_opencl_files}) get_filename_component(OpenCLFileName ${opencl_file} NAME_WE) get_filename_component(OpenCLFileExtension ${opencl_file} EXT) set(opencl_file_copy) if(${OpenCLFileExtension} STREQUAL ".clh") set(opencl_file_copy "${OpenCLFileName}HeaderKernel.cxx.clh") elseif(${OpenCLFileExtension} STREQUAL ".cl") set(opencl_file_copy "${OpenCLFileName}Kernel.cxx.cl") endif() configure_file(${opencl_file} ${CMAKE_CURRENT_BINARY_DIR}/${opencl_file_copy} COPYONLY) list(APPEND opencl_depend_files "${CMAKE_CURRENT_BINARY_DIR}/${opencl_file_copy}") endforeach() #message(STATUS "VAR opencl_depend_files: ${opencl_depend_files}") add_custom_target(${kernel_cxx_class_name}_Target DEPENDS ${opencl_depend_files} ) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${output_file_generated} PROPERTIES GENERATED ON) set(${_src_var} ${${_src_var}} ${output_file_generated}) set_property(TARGET ${kernel_cxx_class_name}_Target PROPERTY FOLDER ${_group_name}) endmacro() #----------------------------------------------------------------------------- # OpenCL interface macros. # write_opencl_kernels() # macro(write_opencl_kernels _opencl_kernels _opencl_src _group_name _merge_kernels_to_one_file) #message(STATUS "write_opencl_kernels macro variables") #message(STATUS "VAR _opencl_kernels: ${_opencl_kernels}") #message(STATUS "VAR _opencl_src: ${_opencl_src}") #message(STATUS "VAR _group_name: ${_group_name}") #message(STATUS "VAR _merge_kernels_to_one_file: ${_merge_kernels_to_one_file}") if(NOT ${_merge_kernels_to_one_file}) foreach(opencl_kernel ${_opencl_kernels}) write_opencl_kernel_to_file(${opencl_kernel} ${_opencl_src} ${_group_name}) endforeach() else() set(merge_to ${ARGV4}) write_opencl_kernels_to_file("${_opencl_kernels}" ${merge_to} ${_opencl_src} ${_group_name}) endif() endmacro() elastix-5.2.0/CMake/elastixVersion.cmake000066400000000000000000000003441474534065100202020ustar00rootroot00000000000000# Elastix version number components. set(ELASTIX_VERSION_MAJOR "5") set(ELASTIX_VERSION_MINOR "2") set(ELASTIX_VERSION_PATCH "0") set(ELASTIX_VERSION "${ELASTIX_VERSION_MAJOR}.${ELASTIX_VERSION_MINOR}.${ELASTIX_VERSION_PATCH}")elastix-5.2.0/CMakeLists.txt000066400000000000000000000534641474534065100157540ustar00rootroot00000000000000# Minimum CMake version. Equal to ITK 5.3 ITK_OLDEST_VALIDATED_POLICIES_VERSION. cmake_minimum_required(VERSION 3.16.3) project(elastix) set(CMAKE_CXX_STANDARD 17) if(BUILD_SHARED_LIBS) message(FATAL_ERROR "Elastix does not support BUILD_SHARED_LIBS") endif() #--------------------------------------------------------------------- cmake_policy(SET CMP0012 NEW) # "if() recognizes numbers and boolean constants" cmake_policy(SET CMP0042 NEW) # "MACOSX_RPATH is enabled by default." #--------------------------------------------------------------------- include(CTest) #--------------------------------------------------------------------- # Allow specifying whether or not the executables are built. option( ELASTIX_BUILD_EXECUTABLE "Build elastix and transformix as executable? (The libraries are always built as well anyway.)" ON ) # The following may make smaller and quicker loading libraries, # that hides unnecessary symbols. Available from CMake 3.0.0. #set(CMAKE_C_VISIBILITY_PRESET hidden) #set(CMAKE_CXX_VISIBILITY_PRESET hidden) #--------------------------------------------------------------------- option(ELASTIX_USE_OPENCL "Use OpenCL enabled GPU" OFF) set(_GPU_depends "") if(ELASTIX_USE_OPENCL) list(APPEND _GPU_depends ITKGPUCommon) endif() # Find ITK. find_package(ITK 5.4 REQUIRED COMPONENTS ITKCommon ITKDisplacementField ITKDistanceMap ITKGDCM ITKImageCompose ITKImageFunction ITKImageGradient ITKImageGrid ITKImageIntensity ITKImageStatistics ITKIOImageBase ITKIOMeshBase ITKIOMeshOBJ ITKIOMeta ITKIOTransformBase ITKIOXML ITKMathematicalMorphology ITKMesh ITKOptimizers ITKRegistrationCommon ITKSmoothing ITKSpatialObjects ITKStatistics ITKTestKernel ITKThresholding ITKTransform ITKTransformFactory ${_GPU_depends} ITKImageIO ITKTransformIO ITKMeshIO ) include(${ITK_USE_FILE}) #--------------------------------------------------------------------- # Add the CMake dir as an additional module path, # so that CMake is able to find the FindPackage modules. list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake) #--------------------------------------------------------------------- # Get version information. include(elastixVersion) # Function for exporting targets include(elastixExportTarget) if (MSVC) add_compile_options(/W3) else() add_compile_options( -Woverloaded-virtual -Wshadow -Wunused-parameter ) endif() #--------------------------------------------------------------------- # Find OpenCL. if(ELASTIX_USE_OPENCL) find_package(OpenCL REQUIRED QUIET) # Make sure ITK is not compiled with GPU extensions on, # since elastix overrides ITK classes with new ones. if(${ITK_USE_GPU}) message(FATAL_ERROR "ITK_USE_GPU: " ${ITK_USE_GPU} "\nERROR: ITK should be compiled with ITK_USE_GPU OFF, as elastix overrides ITK classes.") endif() # Force OPENCL_OPTIMIZATION_MAD_ENABLE to on if(DEFINED OPENCL_OPTIMIZATION_MAD_ENABLE) set(OPENCL_OPTIMIZATION_MAD_ENABLE ON CACHE BOOL "Allow a * b + c to be replaced by a mad. The mad computes a * b + c with reduced accuracy." FORCE) endif() # Include the OpenCL include directories to elastix include_directories(${OPENCL_INCLUDE_DIRS}) # Add some useful macro's include(elastixOpenCL) # Add definition for the OpenCL add_definitions(-DELASTIX_USE_OPENCL) endif() #--------------------------------------------------------------------- # Find Eigen mark_as_advanced(ELASTIX_USE_EIGEN) option(ELASTIX_USE_EIGEN "Use Eigen library." OFF) if(ELASTIX_USE_EIGEN) find_package(Eigen3 REQUIRED) include_directories(${EIGEN3_INCLUDE_DIR}) add_definitions(-DELASTIX_USE_EIGEN) endif() #--------------------------------------------------------------------- # Set single (build-tree) output directories for all executables and libraries. # This makes it easier to create an elxUseFile.cmake, that users can # include in their programs to borrow elastix functionality. if(NOT LIBRARY_OUTPUT_PATH) set(LIBRARY_OUTPUT_PATH ${elastix_BINARY_DIR}/bin CACHE INTERNAL "Single output directory for building all libraries.") endif() if(NOT EXECUTABLE_OUTPUT_PATH) set(EXECUTABLE_OUTPUT_PATH ${elastix_BINARY_DIR}/bin CACHE INTERNAL "Single output directory for building all executables.") endif() # Mark these variables as advanced; their default value is usually fine mark_as_advanced(LIBRARY_OUTPUT_PATH EXECUTABLE_OUTPUT_PATH) #--------------------------------------------------------------------- # Define the install directory names, relative to install prefix # given by CMAKE_INSTALL_PREFIX. All components should use the # same name. Set as variable here for future customization; e.g. # if desire to decorate with Elastix version. # Allow user to customize. mark_as_advanced(ELASTIX_ARCHIVE_DIR ELASTIX_INCLUDE_DIR ELASTIX_LIBRARY_DIR ELASTIX_RUNTIME_DIR) set(ELASTIX_ARCHIVE_DIR "lib" CACHE STRING "Directory for installing archive files; path is relative to CMAKE_INSTALL_PREFIX") set(ELASTIX_INCLUDE_DIR "include" CACHE STRING "Directory for installing include files; path is relative to CMAKE_INSTALL_PREFIX") set(ELASTIX_LIBRARY_DIR "lib" CACHE STRING "Directory for installing library files; path is relative to CMAKE_INSTALL_PREFIX") set(ELASTIX_RUNTIME_DIR "bin" CACHE STRING "Directory for installing runtime files; path is relative to CMAKE_INSTALL_PREFIX") if(NOT ELASTIX_INSTALL_PACKAGE_DIR) set(ELASTIX_INSTALL_PACKAGE_DIR "${ELASTIX_LIBRARY_DIR}/cmake/elastix") endif() #--------------------------------------------------------------------- # Provide options to avoid installing runtime or development components option(ELASTIX_NO_INSTALL_RUNTIME_LIBRARIES "Do not install runtime libraries" OFF) option(ELASTIX_NO_INSTALL_EXECUTABLES "Do not install executables" OFF) option(ELASTIX_NO_INSTALL_DEVELOPMENT "Do not install development headers and static libraries" OFF) mark_as_advanced(ELASTIX_NO_INSTALL_EXECUTABLES ELASTIX_NO_INSTALL_RUNTIME_LIBRARIES ELASTIX_NO_INSTALL_DEVELOPMENT) #--------------------------------------------------------------------- # Check if Mevis DicomTiff support is desired mark_as_advanced(ELASTIX_USE_MEVISDICOMTIFF) option(ELASTIX_USE_MEVISDICOMTIFF "Support MevisLab DicomTiff image format" OFF) #--------------------------------------------------------------------- # Define cmake variable to define extra user component directories # These directories will be added to the list of include directories # and they will be searched for CMakeLists.txt files for further # processing. In these directories, users may put code for their own # components. mark_as_advanced(ELASTIX_USER_COMPONENT_DIRS) set(ELASTIX_USER_COMPONENT_DIRS "" CACHE PATH "directories with user defined elastix components") #--------------------------------------------------------------------- # If IDE supports it, do use folder view. # gcc automatically ignores it. VC Express does not support and gives # annoying warnings when this option is turned on. # VC pro does support it. set(CMAKE_USE_FOLDERS ON CACHE INTERNAL "Use folder view in IDE") if(CMAKE_MAKE_PROGRAM MATCHES ".?VCExpress.?") set(CMAKE_USE_FOLDERS OFF CACHE INTERNAL "Use folder view in IDE") endif() set_property(GLOBAL PROPERTY USE_FOLDERS ${CMAKE_USE_FOLDERS}) #--------------------------------------------------------------------- # Set default build type to Release, if none was specified # Taken from ITK CMake list if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Release' as none was specified.") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #--------------------------------------------------------------------- # Include directories set(elxCommon_INCLUDE_DIRECTORIES ${elastix_SOURCE_DIR}/Common ${elastix_SOURCE_DIR}/Common/CostFunctions ${elastix_SOURCE_DIR}/Common/ImageSamplers ${elastix_SOURCE_DIR}/Common/LineSearchOptimizers ${elastix_SOURCE_DIR}/Common/ParameterFileParser ${elastix_SOURCE_DIR}/Common/Transforms ${elastix_SOURCE_DIR}/Common/MevisDicomTiff ) set(elxCommon_OpenCL_INCLUDE_DIRECTORIES ${elastix_SOURCE_DIR}/Common/OpenCL ${elastix_SOURCE_DIR}/Common/OpenCL/Factories ${elastix_SOURCE_DIR}/Common/OpenCL/ITKimprovements ${elastix_SOURCE_DIR}/Common/OpenCL/Filters ${elastix_SOURCE_DIR}/Common/OpenCL/Kernels ) set(elxCore_INCLUDE_DIRECTORIES ${elastix_SOURCE_DIR}/Core ${elastix_SOURCE_DIR}/Core/Install ${elastix_SOURCE_DIR}/Core/Kernel ${elastix_SOURCE_DIR}/Core/ComponentBaseClasses ${elastix_SOURCE_DIR}/Core/Configuration ${elastix_SOURCE_DIR}/Core/Main ) set(elxComponents_INCLUDE_DIRECTORIES ${elastix_SOURCE_DIR}/Components/FixedImagePyramids ${elastix_SOURCE_DIR}/Components/ImageSamplers ${elastix_SOURCE_DIR}/Components/Interpolators ${elastix_SOURCE_DIR}/Components/Metrics ${elastix_SOURCE_DIR}/Components/MovingImagePyramids ${elastix_SOURCE_DIR}/Components/Optimizers ${elastix_SOURCE_DIR}/Components/Registrations ${elastix_SOURCE_DIR}/Components/ResampleInterpolators ${elastix_SOURCE_DIR}/Components/Resamplers ${elastix_SOURCE_DIR}/Components/Transforms ) set(elxINCLUDE_DIRECTORIES ${elxCommon_INCLUDE_DIRECTORIES} ${elxCore_INCLUDE_DIRECTORIES} ${elxComponents_INCLUDE_DIRECTORIES} ${elastix_BINARY_DIR} ${ELASTIX_USER_COMPONENT_DIRS} ) include_directories(${elxINCLUDE_DIRECTORIES}) # include the OpenCL directories # The key-word BEFORE is important here, because the elastix distribution # contains some files with the same name as in the ITK distribution. # Some bugs were fixed and features added. When these are contributed back # to the ITK the BEFORE keyword is not needed anymore. if(ELASTIX_USE_OPENCL) include_directories(BEFORE ${elxCommon_OpenCL_INCLUDE_DIRECTORIES}) list(APPEND elxINCLUDE_DIRECTORIES ${elxCommon_OpenCL_INCLUDE_DIRECTORIES}) endif() #--------------------------------------------------------------------- # Microsoft specific items if(MSVC) # Kill the anoying MS VS warning about non-safe functions. # They hide real warnings. add_definitions( /D_SCL_SECURE_NO_DEPRECATE /D_CRT_SECURE_NO_DEPRECATE /D_CRT_TIME_FUNCTIONS_NO_DEPRECATE) # Increases address capacity if(WIN32) add_compile_options(/bigobj) endif() endif() #--------------------------------------------------------------------- # Process the sub-directories # Common dir: code that is neither related to the core of elastix or # to specific components. add_subdirectory(Common) # Components: the registration components such as metrics, transforms, # optimizers, etc. add_subdirectory(Components) # Core dir: code that takes care of starting elastix, loading # components, definitions of macros etc. add_subdirectory(Core) #--------------------------------------------------------------------- # Configure the examples set(ELASTIX_DOX_DIR ${elastix_SOURCE_DIR}/dox) set(ELASTIX_TOOLS_DIR ${elastix_BINARY_DIR}/tools) set(ELASTIX_HELP_DIR ${elastix_BINARY_DIR}/help CACHE PATH "path to the doxygen generated help files and the examples") # Copy the examples to the help directory if(WIN32) configure_file( ${ELASTIX_DOX_DIR}/example.bat ${ELASTIX_HELP_DIR}/example.bat COPYONLY) else() configure_file( ${ELASTIX_DOX_DIR}/example ${ELASTIX_HELP_DIR}/example COPYONLY) endif() make_directory(${ELASTIX_HELP_DIR}/exampleinput) make_directory(${ELASTIX_HELP_DIR}/exampleoutput) set(ELX_EXAMPLEINPUTFILES fixed.mhd fixed.raw mask_fixed.mhd mask_fixed.raw mask_moving.mhd mask_moving.raw moving.mhd moving.raw parameters_Affine.txt parameters_BSpline.txt parameters_Rigid.txt parameters_Translation.txt solution_deformedmovingimage.mhd solution_deformedmovingimage.raw ) foreach(ELX_EXAMPLEINPUTFILE ${ELX_EXAMPLEINPUTFILES}) configure_file( ${ELASTIX_DOX_DIR}/exampleinput/${ELX_EXAMPLEINPUTFILE} ${ELASTIX_HELP_DIR}/exampleinput/${ELX_EXAMPLEINPUTFILE} COPYONLY) endforeach() #--------------------------------------------------------------------- # Configure the doxygen-configuration option(BUILD_DOCUMENTATION "Build elastix documentation." OFF) if(${BUILD_DOCUMENTATION}) find_package(Doxygen QUIET) string(COMPARE NOTEQUAL ${DOXYGEN} "DOXYGEN-NOTFOUND" Doxygen_FOUND) if(Doxygen_FOUND) # Set the path to the doxygen configuration source set(ELASTIX_DOXYGEN_DIR ${ELASTIX_DOX_DIR}/doxygen) # Get the version number of doxygen exec_program(${DOXYGEN} ARGS "--version" OUTPUT_VARIABLE ELASTIX_DOXYGEN_VERSION) # Get date if(UNIX OR CYGWIN) exec_program("date '+%d-%m-%Y'" OUTPUT_VARIABLE ELASTIX_DOXYGEN_DATE) endif() if(WIN32) if(NOT CYGWIN AND NOT MINGW) execute_process(COMMAND "${ELASTIX_DOXYGEN_DIR}/doxdate.bat" OUTPUT_VARIABLE ELASTIX_DOXYGEN_DATETEMP) string(REPLACE "/" "-" ELASTIX_DOXYGEN_DATE ${ELASTIX_DOXYGEN_DATETEMP}) endif() endif() # Configure the doxygen configuration configure_file( ${ELASTIX_DOXYGEN_DIR}/doxyfile.in ${ELASTIX_HELP_DIR}/doxyfile.out @ONLY) # Configure the footer of the help website. configure_file( ${ELASTIX_DOXYGEN_DIR}/DoxygenFooter.html.in ${ELASTIX_HELP_DIR}/DoxygenFooter.html @ONLY) # Configure the MainPage.dox configure_file( ${ELASTIX_DOXYGEN_DIR}/MainPage.dox.in ${ELASTIX_HELP_DIR}/MainPage.dox @ONLY) endif() endif() #--------------------------------------------------------------------- # Testing if(BUILD_TESTING) add_subdirectory(Testing) endif() mark_as_advanced(ELASTIX_USE_GTEST) option(ELASTIX_USE_GTEST "Use GoogleTest to test Elastix implementation" OFF) if(ELASTIX_USE_GTEST) enable_testing() add_subdirectory(Common/GTesting) add_subdirectory(Core/Main/GTesting) endif() #--------------------------------------------------------------------- # Packaging mark_as_advanced(ELASTIX_ENABLE_PACKAGER) option(ELASTIX_ENABLE_PACKAGER "Enable elastix packager using CPack" OFF) if(ELASTIX_ENABLE_PACKAGER) # Version information # If the next line is uncommented the package name will be like # elastix-4.3-win64 instead of elastix-4.3.0-win64 set(CPACK_PACKAGE_VERSION_MAJOR ${ELASTIX_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${ELASTIX_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${ELASTIX_VERSION_PATCH}) # Also install the copyright file, since when the user enables packaging # we assume that the package is meant to distribute. install(FILES "${elastix_SOURCE_DIR}/LICENSE" DESTINATION .) install(FILES "${elastix_SOURCE_DIR}/NOTICE" DESTINATION .) # We have split the elastix package into two components: # - the core: elastix and transformix # - the libraries: ANNlib # NOTE: Currently does not work for nsis. A bug related to this # seems to be fixed in the upcoming CMake 2.8.3 # Therefore, disable component support for now. #set(CPACK_COMPONENTS_ALL core libraries Unspecified) # CPACK_ADD_COMPONENT( core # "Core files" # DESCRIPTION "Contains elastix and transformix" # REQUIRED # DEPENDS libraries) # [GROUP group] # [DEPENDS comp1 comp2 ... ] # [INSTALL_TYPES type1 type2 ... ] # [DOWNLOADED] # [ARCHIVE_FILE filename]) #CPACK_ADD_COMPONENT( libraries # "Libraries" # DESCRIPTION "Contains the libraries" # REQUIRED) #set(CPACK_COMPONENT_CORE_DISPLAY_NAME "Core files") #set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") #set(CPACK_COMPONENT_CORE_DESCRIPTION "Contains elastix and transformix") #set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "Contains the libraries") #set(CPACK_COMPONENT_CORE_DEPENDS libraries) #set(CPACK_COMPONENT_CORE_REQUIRED ON) #set(CPACK_COMPONENT_LIBRARIES_REQUIRED ON) # The default package filename is # ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME} # which is like elastix-4.3.0-win64, or elastix-4.3.0-linux-i686 # Currently however we use elastix_windows64_v4.3 # We can change our naming schedule or come close to it using: #set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_SYSTEM_NAME}_v${CPACK_PACKAGE_VERSION}") # but it doesn't work since these variables are not defined yet # Moving include(CPack) to above introduces other errors. #set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_${CMAKE_SYSTEM_NAME}_v${ELASTIX_VERSION}") # also does not work properly. Just use the default for now. # General information set(CPACK_PACKAGE_VENDOR "Stefan Klein and Marius Staring") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "elastix is an image registration toolkit") #set(CPACK_PACKAGE_DESCRIPTION_FILE # "${CMAKE_CURRENT_SOURCE_DIR}/ReadMe.txt") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/NOTICE") # The default install directories: .../elastix_v4.3 set(CPACK_PACKAGE_INSTALL_DIRECTORY "elastix_v${ELASTIX_VERSION_MAJOR}.${ELASTIX_VERSION_MINOR}") set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "elastix_v${ELASTIX_VERSION_MAJOR}.${ELASTIX_VERSION_MINOR}") # Do not include a subdirectory in the zip set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) set(CPACK_SET_DESTDIR "OFF") # Make sure the directory structure is kept in the source zip # and that also the dox and tools directories are included. set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${elastix_SOURCE_DIR};/src;${ELASTIX_DOX_DIR};/dox;${ELASTIX_TOOLS_DIR};/tools") # ?? #set(CPACK_PACKAGE_EXECUTABLES "MyExecutable" "My Executable") # For the windows nsis installer only (is this if-check necessary?) if(WIN32 AND NOT UNIX) # NOTE: There is a bug in NSI that does not handle full unix paths properly. # Make sure there is at least one set of four (4) backlashes # (CMake escapes 2, and the other gets escaped too in some second step) # Set the generators. If left blank the user has all options. set(CPACK_GENERATOR "NSIS;ZIP") # Adding information set(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} elastix") set(CPACK_NSIS_HELP_LINK "http:\\\\\\\\elastix.dev") set(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\elastix.dev") set(CPACK_NSIS_CONTACT "elastix@bigr.nl") set(CPACK_NSIS_PACKAGE_NAME "elastix") set(CPACK_NSIS_DISPLAY_NAME "elastix") # Adding icons and images to make it look nice: # 1 A branding image that will be displayed inside the installer # 2 The icon file(.ico) for the generated install program # 3 The icon file(.ico) for the generated uninstall program set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/dox/art\\\\elastix_logo_full_small.bmp") set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/dox/art\\\\elastix_logo_64.ico") set(CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/dox/art\\\\elastix_logo_64.ico") # Create an option in the installer that asks if elastix # needs to be added to the system path set(CPACK_NSIS_MODIFY_PATH ON) else() # set the generators set(CPACK_GENERATOR "TBZ2;ZIP") # set(CPACK_STRIP_FILES "bin/MyExecutable") #set(CPACK_SOURCE_STRIP_FILES "") endif() # Uncomment the following line if we want to include the system # dll's in the distribution! #include(InstallRequiredSystemLibraries) # This include will do all the work. include(CPack) endif() #--------------------------------------------------------------------- # Make it easier to include elastix functionality in other programs. # The build settings file. (necessary for elastix?) #set(ITK_BUILD_SETTINGS_FILE ${ITK_BINARY_DIR}/ITKBuildSettings.cmake) elastix_export_target(elastix_lib) elastix_export_target(transformix_lib) foreach(LIB IN LISTS AllComponentLibs) elastix_export_target(${LIB}) endforeach() # ELASTIX_ITK_DIR -- used (only) for the Config.cmake file in the build tree # It appears necessary for Windows Azure Pipelines, to replace backslashes by # forward slashes before using the ITK bin directory. string(REPLACE "\\" "/" ELASTIX_ITK_DIR ${ITK_DIR}) # Create the Config.cmake file for the build tree: set(ElastixConfig_TREE "build") set(ElastixConfig_CODE "if(NOT DEFINED ITK_DIR) set(ITK_DIR \"${ELASTIX_ITK_DIR}\") endif() ") set(ELX_CONFIG_INCLUDE_DIRECTORIES "${elxINCLUDE_DIRECTORIES}") configure_file(ElastixConfig.cmake.in ElastixConfig.cmake @ONLY) # Create the Config.cmake file for the install tree: set(ElastixConfig_TREE "install") set(ElastixConfig_CODE "set(Elastix_INSTALL_PREFIX \"\${_ELASTIXConfig_DIR}\")") # Construct the proper number of get_filename_component(... PATH) # calls to compute the installation prefix. string(REGEX REPLACE "/" ";" _count "${ELASTIX_INSTALL_PACKAGE_DIR}") foreach(p ${_count}) set(ElastixConfig_CODE "${ElastixConfig_CODE} get_filename_component(Elastix_INSTALL_PREFIX \"\${Elastix_INSTALL_PREFIX}\" PATH)") endforeach(p) set(ELX_CONFIG_INCLUDE_DIRECTORIES "\${Elastix_INSTALL_PREFIX}/include") foreach(ELX_INCLUDE_DIR IN ITEMS ${elxINCLUDE_DIRECTORIES}) if (NOT ELX_INCLUDE_DIR STREQUAL CMAKE_BINARY_DIR) string(REPLACE ${CMAKE_SOURCE_DIR} "" ELX_INCLUDE_DIR ${ELX_INCLUDE_DIR}) list(APPEND ELX_CONFIG_INCLUDE_DIRECTORIES "\${Elastix_INSTALL_PREFIX}/include${ELX_INCLUDE_DIR}") endif() endforeach() # The Config.cmake file for the install tree should not specify the ITK_DIR. unset(ELASTIX_ITK_DIR) configure_file(ElastixConfig.cmake.in "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/ElastixConfig.cmake" @ONLY) configure_file(ElastixConfigVersion.cmake.in ElastixConfigVersion.cmake @ONLY) configure_file(UseElastix.cmake.in UseElastix.cmake @ONLY) if(NOT ELASTIX_NO_INSTALL_DEVELOPMENT) install(FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/ElastixConfig.cmake" "${PROJECT_BINARY_DIR}/ElastixConfigVersion.cmake" "${PROJECT_BINARY_DIR}/UseElastix.cmake" DESTINATION ${ELASTIX_INSTALL_PACKAGE_DIR} COMPONENT Development ) install(EXPORT ElastixTargets DESTINATION ${ELASTIX_INSTALL_PACKAGE_DIR} COMPONENT Development ) endif() elastix-5.2.0/CODE_OF_CONDUCT.md000066400000000000000000000064751474534065100160130ustar00rootroot00000000000000# Elastix Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at M.Staring "AT" lumc.nl and S.Klein "AT" erasmusmc.nl. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq elastix-5.2.0/CONTRIBUTING.md000066400000000000000000000145701474534065100154400ustar00rootroot00000000000000# Contributing to `elastix` # **Thank you for considering contributing to `elastix`!** ### Do you have questions about the source code? ### * Ask any question about how to use `elastix` on the [mailing list](https://groups.google.com/forum/#!forum/elastix-imageregistration). * Do not open an issue on GitHub for general questions, registration questions, or issues you may have while running `elastix`. _GitHub issues are primarily intended for bug reports and fixes._ * General information about `elastix` can be found on our [wiki](https://github.com/SuperElastix/elastix/wiki) or at the [website](https://elastix.dev/). * [The manual](https://github.com/SuperElastix/elastix/releases/download/5.0.0/elastix-5.0.0-manual.pdf) explains much of the inner workings of image registration. ### Did you find a bug? ### * _Ensure the bug was not already reported_ by searching on GitHub under [Issues](https://github.com/SuperElastix/elastix/issues). * If you're unable to find an open issue addressing the problem, you can [open a new one](https://github.com/SuperElastix/elastix/issues/new). Be sure to include a _title and clear description_, as much relevant information as possible. A _code sample with a test_ demonstrating the expected behavior that is not occurring would be awesome. ### Do you intend to add a new feature or change an existing one? ### * Suggest your change on the `elastix` mailing list. * Do not open an issue on GitHub until you have collected positive feedback about the change. ### Did you write a patch that fixes a bug? ### * Open a new [GitHub pull request](https://github.com/SuperElastix/elastix/pull/new/main) (PR) with the patch. * Make sure the PR is done with respect to the [main branch](https://github.com/SuperElastix/elastix/tree/main). * Ensure the PR description (log message) _clearly describes the problem and solution_. Include the relevant issue number if applicable. One-line messages are fine for small changes, but bigger changes should look like this: $ git commit -m "ENH: A brief summary of the commit > > A paragraph describing what changed and its impact." * We use the following tags for commit messages: - ENH: for functional enhancements of the code - BUG: for fixing runtime issues (crash, segmentation fault, exception, or incorrect result) - COMP: for compilation issues (error or warning) - PERF: for performance improvements - STYLE: a change that does not impact the logic or execution of the code (improve coding style, comments, documentation) The body of the message should clearly describe the motivation of the commit (**what**, **why**, and **how**). In order to ease the task of reviewing commits, the message body should follow the following guidelines: 1. Leave a blank line between the subject and the body. This helps `git log` and `git rebase` work nicely, and allows to smooth generation of release notes. 2. Try to keep the subject line below 73 characters. 3. Capitalize the subject line. 4. Do not end the subject line with a period. 5. Use the imperative mood in the subject line (e.g. `BUG: Add missing spacing`). 6. Use semantic line feeds to separate different ideas, which improves the readability. 7. Be concise, but honor the change: if significant alternative solutions were available, explain why they were discarded. 8. If the commit refers to a topic discussed in elastix' mailing list, or fixes a regression test, provide the link. If it fixes a compiler error, provide a minimal verbatim message of the compiler error. If the commit closes an issue, use the [GitHub issue closing keywords](https://help.github.com/en/articles/closing-issues-using-keywords). Keep in mind that the significant time is invested in reviewing commits and *pull requests*, so following these guidelines will greatly help the people doing reviews. These guidelines are largely inspired by Chris Beam's [How to Write a Commit Message](https://chris.beams.io/posts/git-commit/) post. * Ensure the PR adheres to our [coding conventions](#coding-conventions), the PR code format will be checked with clangFormat on the CI. The hook can be used locally, for details see below. * We will review your PR and possibly suggest changes. All PR will be tested on the GitHub Actions CI. When all lights are green, the PR will be merged. * More information on pull requests can be found [here](https://help.github.com/articles/creating-a-pull-request/) and [here](https://gist.github.com/Chaser324/ce0505fbed06b947d962). ## Pre-commit clangFormat hook ## A pre-commit hook that formats the code in the repository according to the correct clangFormat, is available for git. In order to use the hook, it is has to be moved to the .git/hooks folder locally: For Unix: `cp .githooks/pre-commit .git/hooks` `chmod u+x .git/hooks/pre-commit` For Windows: `copy .githooks\pre-commit .git\hooks` The pre-commit hooks is triggered by any git commit message, if files that don't adhere to the correct clangFormat style are found, these files are changes. Run `git status` or `git diff` to see these changes. Changes, if found, have to be re-added and re-commited with git, make sure to use the --amend flag when re-commiting to keep the commit history clean. ## Coding conventions ## Start reading our code and you'll get the hang of it. We optimize for readability: * We indent using two spaces (soft tabs). This ensures a consistent layout of the code on different text editors. * We ALWAYS put spaces after list items and function arguments (`[1, 2, 3]`, not `[1,2,3]`), around operators (`x += 1`, not `x+=1`), etc. * Use `/** ... */` style documentation. * Member variables of classes should start their name with `m_`. For example: `m_NumberOfIterations`. * Type definitions should start with a capital. ImageType for example, instead of imageType. * This is open source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as smooth as possible. ## Code of conduct ## Please respect our [code of conduct](CODE_OF_CONDUCT.md). Thanks, The `elastix` team elastix-5.2.0/CTestConfig.cmake000066400000000000000000000010241474534065100163470ustar00rootroot00000000000000## This file should be placed in the root directory of your project. ## Then modify the CMakeLists.txt file in the root directory of your ## project to incorporate the testing dashboard. ## # The following are required to uses Dart and the Cdash dashboard ## enable_testing() ## include(CTest) set(CTEST_PROJECT_NAME "elastix") set(CTEST_NIGHTLY_START_TIME "00:01:00 CET") set(CTEST_DROP_METHOD "https") set(CTEST_DROP_SITE "my.cdash.org") set(CTEST_DROP_LOCATION "/submit.php?project=elastix") set(CTEST_DROP_SITE_CDASH TRUE) elastix-5.2.0/Common/000077500000000000000000000000001474534065100144305ustar00rootroot00000000000000elastix-5.2.0/Common/CMakeLists.txt000066400000000000000000000206641474534065100172000ustar00rootroot00000000000000#--------------------------------------------------------------------- project(elxCommon) #--------------------------------------------------------------------- # Sub-projects. add_subdirectory(ParameterFileParser) add_subdirectory(MevisDicomTiff) if(ELASTIX_USE_OPENCL) add_subdirectory(OpenCL) endif() #--------------------------------------------------------------------- # Define lists of files in the subdirectories. set(CommonFiles elxDefaultConstruct.h elxSupportedImageDimensions.h itkAdvancedLinearInterpolateImageFunction.h itkAdvancedLinearInterpolateImageFunction.hxx itkAdvancedRayCastInterpolateImageFunction.h itkAdvancedRayCastInterpolateImageFunction.hxx itkComputeImageExtremaFilter.h itkComputeImageExtremaFilter.hxx itkComputeDisplacementDistribution.h itkComputeDisplacementDistribution.hxx itkComputeJacobianTerms.h itkComputeJacobianTerms.hxx itkComputePreconditionerUsingDisplacementDistribution.h itkComputePreconditionerUsingDisplacementDistribution.hxx itkErodeMaskImageFilter.h itkErodeMaskImageFilter.hxx itkGenericMultiResolutionPyramidImageFilter.h itkGenericMultiResolutionPyramidImageFilter.hxx itkImageFileCastWriter.h itkImageFileCastWriter.hxx itkMeshFileReaderBase.h itkMeshFileReaderBase.hxx itkMultiOrderBSplineDecompositionImageFilter.h itkMultiOrderBSplineDecompositionImageFilter.hxx itkMultiResolutionGaussianSmoothingPyramidImageFilter.h itkMultiResolutionGaussianSmoothingPyramidImageFilter.hxx itkMultiResolutionImageRegistrationMethod2.h itkMultiResolutionImageRegistrationMethod2.hxx itkMultiResolutionShrinkPyramidImageFilter.h itkMultiResolutionShrinkPyramidImageFilter.hxx itkNDImageBase.h itkNDImageTemplate.h itkNDImageTemplate.hxx itkParabolicErodeDilateImageFilter.h itkParabolicErodeDilateImageFilter.hxx itkParabolicErodeImageFilter.h itkParabolicMorphUtils.h itkRecursiveBSplineInterpolationWeightFunction.h itkRecursiveBSplineInterpolationWeightFunction.hxx itkReducedDimensionBSplineInterpolateImageFunction.h itkReducedDimensionBSplineInterpolateImageFunction.hxx itkScaledSingleValuedNonLinearOptimizer.cxx itkScaledSingleValuedNonLinearOptimizer.h itkTransformixInputPointFileReader.h itkTransformixInputPointFileReader.hxx TypeList.h ) set(CostFunctionFiles CostFunctions/itkAdvancedImageToImageMetric.h CostFunctions/itkAdvancedImageToImageMetric.hxx CostFunctions/itkExponentialLimiterFunction.h CostFunctions/itkExponentialLimiterFunction.hxx CostFunctions/itkHardLimiterFunction.h CostFunctions/itkHardLimiterFunction.hxx CostFunctions/itkImageToImageMetricWithFeatures.h CostFunctions/itkImageToImageMetricWithFeatures.hxx CostFunctions/itkLimiterFunctionBase.h CostFunctions/itkMultiInputImageToImageMetricBase.h CostFunctions/itkMultiInputImageToImageMetricBase.hxx CostFunctions/itkParzenWindowHistogramImageToImageMetric.h CostFunctions/itkParzenWindowHistogramImageToImageMetric.hxx CostFunctions/itkScaledSingleValuedCostFunction.cxx CostFunctions/itkScaledSingleValuedCostFunction.h CostFunctions/itkSingleValuedPointSetToPointSetMetric.h CostFunctions/itkSingleValuedPointSetToPointSetMetric.hxx CostFunctions/itkTransformPenaltyTerm.h CostFunctions/itkTransformPenaltyTerm.hxx ) set(TransformFiles Transforms/elxTransformFactoryRegistration.h Transforms/elxTransformFactoryRegistration.cxx Transforms/elxTransformIO.h Transforms/elxTransformIO.cxx Transforms/itkAdvancedBSplineDeformableTransformBase.h Transforms/itkAdvancedBSplineDeformableTransformBase.hxx Transforms/itkAdvancedBSplineDeformableTransform.h Transforms/itkAdvancedBSplineDeformableTransform.hxx Transforms/itkAdvancedCombinationTransform.h Transforms/itkAdvancedCombinationTransform.hxx Transforms/itkAdvancedEuler3DTransform.h Transforms/itkAdvancedEuler3DTransform.hxx Transforms/itkAdvancedIdentityTransform.h Transforms/itkAdvancedImageMomentsCalculator.h Transforms/itkAdvancedImageMomentsCalculator.hxx Transforms/itkAdvancedMatrixOffsetTransformBase.h Transforms/itkAdvancedMatrixOffsetTransformBase.hxx Transforms/itkAdvancedRigid2DTransform.h Transforms/itkAdvancedRigid2DTransform.hxx Transforms/itkAdvancedRigid3DTransform.h Transforms/itkAdvancedRigid3DTransform.hxx Transforms/itkAdvancedSimilarity2DTransform.h Transforms/itkAdvancedSimilarity2DTransform.hxx Transforms/itkAdvancedSimilarity3DTransform.h Transforms/itkAdvancedSimilarity3DTransform.hxx Transforms/itkAdvancedTransform.h Transforms/itkAdvancedTransform.hxx Transforms/itkAdvancedTranslationTransform.h Transforms/itkAdvancedTranslationTransform.hxx Transforms/itkAdvancedVersorTransform.h Transforms/itkAdvancedVersorTransform.hxx Transforms/itkAdvancedVersorRigid3DTransform.h Transforms/itkAdvancedVersorRigid3DTransform.hxx Transforms/itkBSplineDerivativeKernelFunction2.h Transforms/itkBSplineInterpolationDerivativeWeightFunction.h Transforms/itkBSplineInterpolationDerivativeWeightFunction.hxx Transforms/itkBSplineInterpolationSecondOrderDerivativeWeightFunction.h Transforms/itkBSplineInterpolationSecondOrderDerivativeWeightFunction.hxx Transforms/itkBSplineInterpolationWeightFunction2.h Transforms/itkBSplineInterpolationWeightFunction2.hxx Transforms/itkBSplineInterpolationWeightFunctionBase.h Transforms/itkBSplineInterpolationWeightFunctionBase.hxx Transforms/itkBSplineKernelFunction2.h Transforms/itkBSplineSecondOrderDerivativeKernelFunction2.h Transforms/itkCyclicBSplineDeformableTransform.h Transforms/itkCyclicBSplineDeformableTransform.hxx Transforms/itkCyclicGridScheduleComputer.h Transforms/itkCyclicGridScheduleComputer.hxx Transforms/itkEulerTransform.h Transforms/itkGridScheduleComputer.h Transforms/itkGridScheduleComputer.hxx Transforms/itkRecursiveBSplineTransform.hxx Transforms/itkRecursiveBSplineTransform.h Transforms/itkRecursiveBSplineTransformImplementation.h Transforms/itkStackTransform.h Transforms/itkStackTransform.hxx Transforms/itkTransformToDeterminantOfSpatialJacobianSource.h Transforms/itkTransformToDeterminantOfSpatialJacobianSource.hxx Transforms/itkTransformToSpatialJacobianSource.h Transforms/itkTransformToSpatialJacobianSource.hxx Transforms/itkUpsampleBSplineParametersFilter.h Transforms/itkUpsampleBSplineParametersFilter.hxx ) set(LineSearchOptimizersFiles LineSearchOptimizers/itkMoreThuenteLineSearchOptimizer.h LineSearchOptimizers/itkMoreThuenteLineSearchOptimizer.cxx LineSearchOptimizers/itkLineSearchOptimizer.h LineSearchOptimizers/itkLineSearchOptimizer.cxx ) set(ImageSamplersFiles ImageSamplers/itkImageFullSampler.h ImageSamplers/itkImageFullSampler.hxx ImageSamplers/itkImageGridSampler.h ImageSamplers/itkImageGridSampler.hxx ImageSamplers/itkImageRandomCoordinateSampler.h ImageSamplers/itkImageRandomCoordinateSampler.hxx ImageSamplers/itkImageRandomSampler.h ImageSamplers/itkImageRandomSampler.hxx ImageSamplers/itkImageRandomSamplerBase.h ImageSamplers/itkImageRandomSamplerSparseMask.h ImageSamplers/itkImageRandomSamplerSparseMask.hxx ImageSamplers/itkImageSample.h ImageSamplers/itkImageSamplerBase.h ImageSamplers/itkImageSamplerBase.hxx ImageSamplers/itkMultiInputImageRandomCoordinateSampler.h ImageSamplers/itkMultiInputImageRandomCoordinateSampler.hxx ImageSamplers/itkVectorContainerSource.h ImageSamplers/itkVectorContainerSource.hxx ImageSamplers/itkVectorDataContainer.h ImageSamplers/itkVectorDataContainer.hxx ) #--------------------------------------------------------------------- # Construct source groups for nice visualisation in Visual Studio. source_group("Common" FILES ${CommonFiles}) source_group("CostFunctions" FILES ${CostFunctionFiles}) source_group("Transforms" FILES ${TransformFiles}) source_group("LineSearchOptimizers" FILES ${LineSearchOptimizersFiles}) source_group("ImageSamplers" FILES ${ImageSamplersFiles}) #--------------------------------------------------------------------- # Create the elxCommon library. add_library(elxCommon STATIC ${CommonFiles} ${CostFunctionFiles} ${TransformFiles} ${LineSearchOptimizersFiles} ${ImageSamplersFiles} ) if(NOT ELASTIX_NO_INSTALL_DEVELOPMENT) install(TARGETS elxCommon ARCHIVE DESTINATION ${ELASTIX_ARCHIVE_DIR} LIBRARY DESTINATION ${ELASTIX_LIBRARY_DIR} RUNTIME DESTINATION ${ELASTIX_RUNTIME_DIR} COMPONENT Development ) endif() #--------------------------------------------------------------------- # Link against other libraries. target_link_libraries(elxCommon ${ITK_LIBRARIES} ) elastix-5.2.0/Common/CostFunctions/000077500000000000000000000000001474534065100172315ustar00rootroot00000000000000elastix-5.2.0/Common/CostFunctions/itkAdvancedImageToImageMetric.h000066400000000000000000000661311474534065100252030ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkAdvancedImageToImageMetric_h #define itkAdvancedImageToImageMetric_h #include "itkImageToImageMetric.h" #include "itkImageSamplerBase.h" #include "itkGradientImageFilter.h" #include "itkBSplineInterpolateImageFunction.h" #include "itkReducedDimensionBSplineInterpolateImageFunction.h" #include "itkAdvancedLinearInterpolateImageFunction.h" #include "itkLimiterFunctionBase.h" #include "itkFixedArray.h" #include "itkAdvancedTransform.h" #include "itkImageMaskSpatialObject.h" // Needed for checking for B-spline for faster implementation #include "itkAdvancedBSplineDeformableTransform.h" #include "itkAdvancedCombinationTransform.h" #include #include // For unique_ptr. #include namespace itk { /** \class AdvancedImageToImageMetric * * \brief An extension of the ITK ImageToImageMetric. It is the intended base * class for all elastix metrics. * * This class inherits from the itk::ImageToImageMetric. The additional features of * this class that makes it an AdvancedImageToImageMetric are: * \li The use of an ImageSampler, which selects the fixed image samples over which * the metric is evaluated. In the derived metric you simply need to loop over * the image sample container, instead over the fixed image. This way it is easy * to create different samplers, without the derived metric needing to know. * \li Gray value limiters: for some metrics it is important to know the range of expected * gray values in the fixed and moving image, beforehand. However, when a third order * B-spline interpolator is used to interpolate the images, the interpolated values may * be larger than the range of voxel values, because of so-called overshoot. The * gray-value limiters make sure this doesn't happen. * \li Fast implementation when a B-spline transform is used. The B-spline transform * has a sparse Jacobian. The AdvancedImageToImageMetric provides functions that make * it easier for inheriting metrics to exploit this fact. * \li MovingImageDerivativeScales: an experimental option, which allows scaling of the * moving image derivatives. This is a kind of fast hack, which makes it possible to * avoid transformation in one direction (x, y, or z). Do not use this functionality * unless you have a good reason for it... * \li Some convenience functions are provided, such as the IsInsideMovingMask * and CheckNumberOfSamples. * * The parameters used in this class are: * \parameter MovingImageDerivativeScales: scale the moving image derivatives. Use\n * (MovingImageDerivativeScales 1 1 0)\n * to penalize deformations in the z-direction. The default value is that * this feature is not used. * * \ingroup RegistrationMetrics * */ template class ITK_TEMPLATE_EXPORT AdvancedImageToImageMetric : public ImageToImageMetric { public: ITK_DISALLOW_COPY_AND_MOVE(AdvancedImageToImageMetric); /** Standard class typedefs. */ using Self = AdvancedImageToImageMetric; using Superclass = ImageToImageMetric; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Run-time type information (and related methods). */ itkTypeMacro(AdvancedImageToImageMetric, ImageToImageMetric); /** Constants for the image dimensions. */ itkStaticConstMacro(MovingImageDimension, unsigned int, TMovingImage::ImageDimension); itkStaticConstMacro(FixedImageDimension, unsigned int, TFixedImage::ImageDimension); /** Typedefs from the superclass. */ using typename Superclass::CoordinateRepresentationType; using typename Superclass::MovingImageType; using typename Superclass::MovingImagePixelType; using MovingImagePointer = typename MovingImageType::Pointer; using typename Superclass::MovingImageConstPointer; using typename Superclass::FixedImageType; using FixedImagePointer = typename FixedImageType::Pointer; using typename Superclass::FixedImageConstPointer; using typename Superclass::FixedImageRegionType; using typename Superclass::TransformType; using typename Superclass::TransformPointer; using typename Superclass::InputPointType; using typename Superclass::OutputPointType; using typename Superclass::TransformParametersType; using typename Superclass::TransformJacobianType; using typename Superclass::InterpolatorType; using typename Superclass::InterpolatorPointer; using typename Superclass::RealType; using typename Superclass::GradientPixelType; using typename Superclass::GradientImageType; using typename Superclass::GradientImagePointer; // Overrule the mask type from its base class, ITK ImageToImageMetric. using FixedImageMaskType = ImageMaskSpatialObject; using FixedImageMaskPointer = SmartPointer; using FixedImageMaskConstPointer = SmartPointer; using MovingImageMaskType = ImageMaskSpatialObject; using MovingImageMaskPointer = SmartPointer; using MovingImageMaskConstPointer = SmartPointer; using typename Superclass::MeasureType; using typename Superclass::DerivativeType; using DerivativeValueType = typename DerivativeType::ValueType; using typename Superclass::ParametersType; /** Some useful extra typedefs. */ using FixedImagePixelType = typename FixedImageType::PixelType; using MovingImageRegionType = typename MovingImageType::RegionType; using MovingImageDerivativeScalesType = FixedArray; /** Typedefs for the ImageSampler. */ using ImageSamplerType = ImageSamplerBase; using ImageSamplerPointer = typename ImageSamplerType::Pointer; using ImageSampleContainerType = typename ImageSamplerType::OutputVectorContainerType; using ImageSampleContainerPointer = typename ImageSamplerType::OutputVectorContainerPointer; /** Typedefs for Limiter support. */ using FixedImageLimiterType = LimiterFunctionBase; using FixedImageLimiterPointer = typename FixedImageLimiterType::Pointer; using FixedImageLimiterOutputType = typename FixedImageLimiterType::OutputType; using MovingImageLimiterType = LimiterFunctionBase; using MovingImageLimiterPointer = typename MovingImageLimiterType::Pointer; using MovingImageLimiterOutputType = typename MovingImageLimiterType::OutputType; /** Advanced transform. */ using ScalarType = typename TransformType::ScalarType; using AdvancedTransformType = AdvancedTransform; using NumberOfParametersType = typename AdvancedTransformType::NumberOfParametersType; /** Typedef's for the B-spline transform. */ using CombinationTransformType = AdvancedCombinationTransform; using BSplineOrder1TransformType = AdvancedBSplineDeformableTransform; using BSplineOrder2TransformType = AdvancedBSplineDeformableTransform; using BSplineOrder3TransformType = AdvancedBSplineDeformableTransform; using BSplineOrder1TransformPointer = typename BSplineOrder1TransformType::Pointer; using BSplineOrder2TransformPointer = typename BSplineOrder2TransformType::Pointer; using BSplineOrder3TransformPointer = typename BSplineOrder3TransformType::Pointer; /** Typedef for multi-threading. */ using ThreadInfoType = MultiThreaderBase::WorkUnitInfo; /** Public methods ********************/ virtual void SetFixedImageMask(const FixedImageMaskType * const arg) { assert(arg == nullptr || typeid(*arg) == typeid(FixedImageMaskType)); Superclass::SetFixedImageMask(arg); } virtual void SetMovingImageMask(const MovingImageMaskType * const arg) { assert(arg == nullptr || typeid(*arg) == typeid(MovingImageMaskType)); Superclass::SetMovingImageMask(arg); } const FixedImageMaskType * GetFixedImageMask() const override { const auto * const mask = Superclass::GetFixedImageMask(); assert(mask == nullptr || typeid(*mask) == typeid(FixedImageMaskType)); return static_cast(mask); } const MovingImageMaskType * GetMovingImageMask() const override { const auto * const mask = Superclass::GetMovingImageMask(); assert(mask == nullptr || typeid(*mask) == typeid(MovingImageMaskType)); return static_cast(mask); } /** Set the transform, of advanced type. */ virtual void SetTransform(AdvancedTransformType * arg) { this->Superclass::SetTransform(arg); if (m_AdvancedTransform != arg) { m_AdvancedTransform = arg; this->Modified(); } } /** Get the advanced transform. */ AdvancedTransformType * GetTransform() override { return m_AdvancedTransform.GetPointer(); } /** Get the advanced transform. Const overload. */ const AdvancedTransformType * GetTransform() const override { return m_AdvancedTransform.GetPointer(); } /** Set/Get the image sampler. */ itkSetObjectMacro(ImageSampler, ImageSamplerType); ImageSamplerType * GetImageSampler() const { return m_ImageSampler.GetPointer(); } /** Inheriting classes can specify whether they use the image sampler functionality; * This method allows the user to inspect this setting. */ itkGetConstMacro(UseImageSampler, bool); /** Set/Get the required ratio of valid samples; default 0.25. * When less than this ratio*numberOfSamplesTried samples map * inside the moving image buffer, an exception will be thrown. */ itkSetMacro(RequiredRatioOfValidSamples, double); itkGetConstMacro(RequiredRatioOfValidSamples, double); /** Set/Get the Moving/Fixed limiter. Its thresholds and bounds are set by the metric. * Setting a limiter is only mandatory if GetUse{Fixed,Moving}Limiter() returns true. */ itkSetObjectMacro(MovingImageLimiter, MovingImageLimiterType); itkGetConstObjectMacro(MovingImageLimiter, MovingImageLimiterType); itkSetObjectMacro(FixedImageLimiter, FixedImageLimiterType); itkGetConstObjectMacro(FixedImageLimiter, FixedImageLimiterType); /** A percentage that defines how much the gray value range is extended * maxlimit = max + LimitRangeRatio * (max - min) * minlimit = min - LimitRangeRatio * (max - min) * Default: 0.01; * If you use a nearest neighbor or linear interpolator, * set it to zero and use a hard limiter. */ itkSetMacro(MovingLimitRangeRatio, double); itkGetConstMacro(MovingLimitRangeRatio, double); itkSetMacro(FixedLimitRangeRatio, double); itkGetConstMacro(FixedLimitRangeRatio, double); /** Inheriting classes can specify whether they use the image limiter functionality. * This method allows the user to inspect this setting. */ itkGetConstMacro(UseFixedImageLimiter, bool); itkGetConstMacro(UseMovingImageLimiter, bool); /** You may specify a scaling vector for the moving image derivatives. * If the UseMovingImageDerivativeScales is true, the moving image derivatives * are multiplied by the moving image derivative scales (element-wise) * You may use this to avoid deformations in the z-dimension, for example, * by setting the moving image derivative scales to (1,1,0). * This is a rather experimental feature. In most cases you do not need it. */ itkSetMacro(UseMovingImageDerivativeScales, bool); itkGetConstMacro(UseMovingImageDerivativeScales, bool); itkSetMacro(ScaleGradientWithRespectToMovingImageOrientation, bool); itkGetConstMacro(ScaleGradientWithRespectToMovingImageOrientation, bool); itkSetMacro(MovingImageDerivativeScales, MovingImageDerivativeScalesType); itkGetConstReferenceMacro(MovingImageDerivativeScales, MovingImageDerivativeScalesType); /** Initialize the Metric by making sure that all the components * are present and plugged together correctly. * \li Call the superclass' implementation * \li Cache the number of transform parameters * \li Initialize the image sampler, if used. * \li Check if a B-spline interpolator has been set * \li Check if an AdvancedTransform has been set */ void Initialize() override; /** Switch the function BeforeThreadedGetValueAndDerivative on or off. */ itkSetMacro(UseMetricSingleThreaded, bool); itkGetConstReferenceMacro(UseMetricSingleThreaded, bool); itkBooleanMacro(UseMetricSingleThreaded); /** Select the use of multi-threading*/ // \todo: maybe these can be united, check base class. itkSetMacro(UseMultiThread, bool); itkGetConstReferenceMacro(UseMultiThread, bool); itkBooleanMacro(UseMultiThread); /** Contains calls from GetValueAndDerivative that are thread-unsafe, * together with preparation for multi-threading. * Note that the only reason why this function is not protected, is * because the ComboMetric needs to call it. */ virtual void BeforeThreadedGetValueAndDerivative(const TransformParametersType & parameters) const; protected: /** Constructor. */ AdvancedImageToImageMetric(); /** Destructor. */ ~AdvancedImageToImageMetric() override = default; /** PrintSelf. */ void PrintSelf(std::ostream & os, Indent indent) const override; /** Protected Typedefs ******************/ /** Typedefs for indices and points. */ using FixedImageIndexType = typename FixedImageType::IndexType; using FixedImageIndexValueType = typename FixedImageIndexType::IndexValueType; using MovingImageIndexType = typename MovingImageType::IndexType; using FixedImagePointType = typename TransformType::InputPointType; using MovingImagePointType = typename TransformType::OutputPointType; using MovingImageContinuousIndexType = typename InterpolatorType::ContinuousIndexType; /** Typedefs used for computing image derivatives. */ using BSplineInterpolatorType = BSplineInterpolateImageFunction; using BSplineInterpolatorPointer = typename BSplineInterpolatorType::Pointer; using BSplineInterpolatorFloatType = BSplineInterpolateImageFunction; using BSplineInterpolatorFloatPointer = typename BSplineInterpolatorFloatType::Pointer; using ReducedBSplineInterpolatorType = ReducedDimensionBSplineInterpolateImageFunction; using ReducedBSplineInterpolatorPointer = typename ReducedBSplineInterpolatorType::Pointer; using LinearInterpolatorType = AdvancedLinearInterpolateImageFunction; using LinearInterpolatorPointer = typename LinearInterpolatorType::Pointer; using MovingImageDerivativeType = typename BSplineInterpolatorType::CovariantVectorType; /** Typedefs for support of sparse Jacobians and compact support of transformations. */ using NonZeroJacobianIndicesType = typename AdvancedTransformType::NonZeroJacobianIndicesType; /** Protected Variables **************/ /** Variables for ImageSampler support. m_ImageSampler is mutable, * because it is changed in the GetValue(), etc, which are const functions. */ mutable ImageSamplerPointer m_ImageSampler{ nullptr }; /** Variables to store the AdvancedTransform. */ typename AdvancedTransformType::Pointer m_AdvancedTransform{ nullptr }; /** Member variable for TransformPenaltyTerm::CheckForBSplineTransform2 */ mutable bool m_TransformIsBSpline{ false }; /** Variables for the Limiters. */ FixedImagePixelType m_FixedImageTrueMin{ 0 }; FixedImagePixelType m_FixedImageTrueMax{ 1 }; MovingImagePixelType m_MovingImageTrueMin{ 0 }; MovingImagePixelType m_MovingImageTrueMax{ 1 }; FixedImageLimiterOutputType m_FixedImageMinLimit{ 0 }; FixedImageLimiterOutputType m_FixedImageMaxLimit{ 1 }; MovingImageLimiterOutputType m_MovingImageMinLimit{ 0 }; MovingImageLimiterOutputType m_MovingImageMaxLimit{ 1 }; /** Multi-threaded metric computation. */ /** Multi-threaded version of GetValue(). */ virtual void ThreadedGetValue(ThreadIdType) const {} /** Finalize multi-threaded metric computation. */ virtual void AfterThreadedGetValue(MeasureType &) const {} /** GetValue threader callback function. */ static ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION GetValueThreaderCallback(void * arg); /** Launch MultiThread GetValue. */ void LaunchGetValueThreaderCallback() const; /** Multi-threaded version of GetValueAndDerivative(). */ virtual void ThreadedGetValueAndDerivative(ThreadIdType) const {} /** Finalize multi-threaded metric computation. */ virtual void AfterThreadedGetValueAndDerivative(MeasureType &, DerivativeType &) const {} /** GetValueAndDerivative threader callback function. */ static ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION GetValueAndDerivativeThreaderCallback(void * arg); /** Launch MultiThread GetValueAndDerivative. */ void LaunchGetValueAndDerivativeThreaderCallback() const; /** AccumulateDerivatives threader callback function. */ static ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION AccumulateDerivativesThreaderCallback(void * arg); /** Variables for multi-threading. */ bool m_UseMetricSingleThreaded{ true }; bool m_UseMultiThread{ false }; /** Helper structs that multi-threads the computation of * the metric derivative using ITK threads. */ struct MultiThreaderParameterType { // To give the threads access to all members. AdvancedImageToImageMetric * st_Metric; // Used for accumulating derivatives DerivativeValueType * st_DerivativePointer; DerivativeValueType st_NormalizationFactor; }; mutable MultiThreaderParameterType m_ThreaderMetricParameters{}; /** Most metrics will perform multi-threading by letting * each thread compute a part of the value and derivative. * * These parameters are initialized at every call of GetValueAndDerivative * in the function InitializeThreadingParameters(). Since GetValueAndDerivative * is const, also InitializeThreadingParameters should be const, and therefore * these member variables are mutable. */ // test per thread struct with padding and alignment struct GetValueAndDerivativePerThreadStruct { SizeValueType st_NumberOfPixelsCounted; MeasureType st_Value; DerivativeType st_Derivative; }; itkPadStruct(ITK_CACHE_LINE_ALIGNMENT, GetValueAndDerivativePerThreadStruct, PaddedGetValueAndDerivativePerThreadStruct); itkAlignedTypedef(ITK_CACHE_LINE_ALIGNMENT, PaddedGetValueAndDerivativePerThreadStruct, AlignedGetValueAndDerivativePerThreadStruct); mutable std::unique_ptr m_GetValueAndDerivativePerThreadVariables{ nullptr }; mutable ThreadIdType m_GetValueAndDerivativePerThreadVariablesSize{ 0 }; /** Initialize some multi-threading related parameters. */ virtual void InitializeThreadingParameters() const; /** Protected methods ************** */ /** Methods for image sampler support **********/ /** Initialize variables related to the image sampler; called by Initialize. */ virtual void InitializeImageSampler(); /** Inheriting classes can specify whether they use the image sampler functionality * Make sure to set it before calling Initialize; default: false. */ itkSetMacro(UseImageSampler, bool); /** Check if enough samples have been found to compute a reliable * estimate of the value/derivative; throws an exception if not. */ void CheckNumberOfSamples(unsigned long wanted, unsigned long found) const; /** Methods for image derivative evaluation support **********/ /** Initialize variables for image derivative computation; this * method is called by Initialize. */ void CheckForBSplineInterpolator(); /** Compute the image value (and possibly derivative) at a transformed point. * Checks if the point lies within the moving image buffer (bool return). * If no gradient is wanted, set the gradient argument to 0. * If a BSplineInterpolationFunction or AdvacnedLinearInterpolationFunction * is used, this class obtains image derivatives from the B-spline or linear * interpolator. Otherwise, image derivatives are computed using nearest * neighbor interpolation of a precomputed (central difference) gradient image. */ virtual bool EvaluateMovingImageValueAndDerivative(const MovingImagePointType & mappedPoint, RealType & movingImageValue, MovingImageDerivativeType * gradient) const { return EvaluateMovingImageValueAndDerivativeWithOptionalThreadId(mappedPoint, movingImageValue, gradient); } /* A faster version of `EvaluateMovingImageValueAndDerivative`: Non-virtual, using multithreading, and doing less * dynamic memory allocation/decallocation operations, internally. */ bool FastEvaluateMovingImageValueAndDerivative(const MovingImagePointType & mappedPoint, RealType & movingImageValue, MovingImageDerivativeType * gradient, const ThreadIdType threadId) const { return EvaluateMovingImageValueAndDerivativeWithOptionalThreadId(mappedPoint, movingImageValue, gradient, threadId); } /** Computes the inner product of transform Jacobian with moving image gradient. * The results are stored in imageJacobian, which is supposed * to have the right size (same length as Jacobian's number of columns). */ virtual void EvaluateTransformJacobianInnerProduct(const TransformJacobianType & jacobian, const MovingImageDerivativeType & movingImageDerivative, DerivativeType & imageJacobian) const; /** Methods to support transforms with sparse Jacobians, like the BSplineTransform **********/ /** Check if the transform is an AdvancedTransform. Called by Initialize. * If so, we can speed up derivative calculations by only inspecting * the parameters in the support region of a point. */ void CheckForAdvancedTransform(); /** Check if the transform is a B-spline. Called by Initialize. */ void CheckForBSplineTransform() const; /** Transform a point from FixedImage domain to MovingImage domain. */ MovingImagePointType TransformPoint(const FixedImagePointType & fixedImagePoint) const; /** This function returns a reference to the transform Jacobians. * This is either a reference to the full TransformJacobian or * a reference to a sparse Jacobians. * The m_NonZeroJacobianIndices contains the indices that are nonzero. * The length of NonZeroJacobianIndices is set in the CheckForAdvancedTransform * function. */ bool EvaluateTransformJacobian(const FixedImagePointType & fixedImagePoint, TransformJacobianType & jacobian, NonZeroJacobianIndicesType & nzji) const; /** Convenience method: check if point is inside the moving mask. *****************/ virtual bool IsInsideMovingMask(const MovingImagePointType & point) const; /** Initialize the {Fixed,Moving}[True]{Max,Min}[Limit] and the {Fixed,Moving}ImageLimiter * Only does something when Use{Fixed,Moving}Limiter is set to true; */ void InitializeLimiters(); /** Inheriting classes can specify whether they use the image limiter functionality * Make sure to set it before calling Initialize; default: false. */ itkSetMacro(UseFixedImageLimiter, bool); itkSetMacro(UseMovingImageLimiter, bool); double m_FixedLimitRangeRatio{ 0.01 }; double m_MovingLimitRangeRatio{ 0.01 }; // Prevent accidentally calling SetFixedImageMask or SetMovingImageMask through the ITK ImageToImageMetric interface. void SetFixedImageMask(typename Superclass::FixedImageMaskType *) final { itkExceptionMacro("Intentionally left unimplemented!"); } void SetFixedImageMask(const typename Superclass::FixedImageMaskType *) final { itkExceptionMacro("Intentionally left unimplemented!"); } void SetMovingImageMask(typename Superclass::MovingImageMaskType *) final { itkExceptionMacro("Intentionally left unimplemented!"); } void SetMovingImageMask(const typename Superclass::MovingImageMaskType *) final { itkExceptionMacro("Intentionally left unimplemented!"); } // Protected using-declaration, to avoid `-Woverloaded-virtual` warnings from GCC (GCC 11.4) or clang (macos-12). using Superclass::SetTransform; private: template bool EvaluateMovingImageValueAndDerivativeWithOptionalThreadId(const MovingImagePointType & mappedPoint, RealType & movingImageValue, MovingImageDerivativeType * gradient, const TOptionalThreadId... optionalThreadId) const; /** Private member variables for limiters and for image derivative computation. */ FixedImageLimiterPointer m_FixedImageLimiter{ nullptr }; MovingImageLimiterPointer m_MovingImageLimiter{ nullptr }; LinearInterpolatorPointer m_LinearInterpolator{ nullptr }; BSplineInterpolatorPointer m_BSplineInterpolator{ nullptr }; BSplineInterpolatorFloatPointer m_BSplineInterpolatorFloat{ nullptr }; ReducedBSplineInterpolatorPointer m_ReducedBSplineInterpolator{ nullptr }; /** Other private member variables. */ bool m_UseImageSampler{ false }; bool m_UseFixedImageLimiter{ false }; bool m_UseMovingImageLimiter{ false }; double m_RequiredRatioOfValidSamples{ 0.25 }; bool m_UseMovingImageDerivativeScales{ false }; bool m_ScaleGradientWithRespectToMovingImageOrientation{ false }; MovingImageDerivativeScalesType m_MovingImageDerivativeScales{ MovingImageDerivativeScalesType::Filled(1.0) }; // Private using-declarations, to avoid `-Woverloaded-virtual` warnings from GCC (GCC 11.4) or clang (macos-12). using Superclass::TransformPoint; struct DummyMask {}; // Prevent accidentally accessing m_FixedImageMask or m_MovingImageMask directly from the ITK's ImageToImageMetric. static constexpr DummyMask m_FixedImageMask{}; static constexpr DummyMask m_MovingImageMask{}; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkAdvancedImageToImageMetric.hxx" #endif #endif // end #ifndef itkAdvancedImageToImageMetric_h elastix-5.2.0/Common/CostFunctions/itkAdvancedImageToImageMetric.hxx000066400000000000000000000740651474534065100255700ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef _itkAdvancedImageToImageMetric_hxx #define _itkAdvancedImageToImageMetric_hxx #include "itkAdvancedImageToImageMetric.h" #include "elxDefaultConstruct.h" #include "itkAdvancedRayCastInterpolateImageFunction.h" #include "itkComputeImageExtremaFilter.h" #include // For min. #include namespace itk { /** * ********************* Constructor **************************** */ template AdvancedImageToImageMetric::AdvancedImageToImageMetric() { /** Don't use the default gradient image as implemented by ITK. * It uses a Gaussian derivative, which introduces extra smoothing, * which may not always be desired. Also, when the derivatives are * computed using Gaussian filtering, the gray-values should also be * blurred, to have a consistent 'image model'. */ this->SetComputeGradient(false); /** Initialize the m_ThreaderMetricParameters. */ m_ThreaderMetricParameters.st_Metric = this; } // end Constructor /** * ********************* Initialize **************************** */ template void AdvancedImageToImageMetric::Initialize() { /** Initialize transform, interpolator, etc. */ Superclass::Initialize(); /** Setup the parameters for the gray value limiters. */ this->InitializeLimiters(); /** Connect the image sampler */ this->InitializeImageSampler(); /** Check if the interpolator is a B-spline interpolator. */ this->CheckForBSplineInterpolator(); /** Check if the transform is an advanced transform. */ this->CheckForAdvancedTransform(); /** Check if the transform is a B-spline transform. */ this->CheckForBSplineTransform(); /** Initialize some threading related parameters. */ if (m_UseMultiThread) { this->InitializeThreadingParameters(); const auto setNumberOfWorkUnitsIfNotNull = [this](const auto bsplineInterpolator) { if (!bsplineInterpolator.IsNull()) { bsplineInterpolator->SetNumberOfWorkUnits(this->Superclass::GetNumberOfWorkUnits()); } }; setNumberOfWorkUnitsIfNotNull(m_BSplineInterpolator); setNumberOfWorkUnitsIfNotNull(m_BSplineInterpolatorFloat); } } // end Initialize() /** * ********************* InitializeThreadingParameters **************************** */ template void AdvancedImageToImageMetric::InitializeThreadingParameters() const { const ThreadIdType numberOfThreads = Self::GetNumberOfWorkUnits(); /** Resize and initialize the threading related parameters. * The SetSize() functions do not resize the data when this is not * needed, which saves valuable re-allocation time. * * This function is only to be called at the start of each resolution. * Re-initialization of the potentially large vectors is performed after * each iteration, in the accumulate functions, in a multi-threaded fashion. * This has performance benefits for larger vector sizes. */ /** Only resize the array of structs when needed. */ if (m_GetValueAndDerivativePerThreadVariablesSize != numberOfThreads) { m_GetValueAndDerivativePerThreadVariables.reset(new AlignedGetValueAndDerivativePerThreadStruct[numberOfThreads]); m_GetValueAndDerivativePerThreadVariablesSize = numberOfThreads; } /** Some initialization. */ for (ThreadIdType i = 0; i < numberOfThreads; ++i) { m_GetValueAndDerivativePerThreadVariables[i].st_NumberOfPixelsCounted = SizeValueType{}; m_GetValueAndDerivativePerThreadVariables[i].st_Value = MeasureType{}; m_GetValueAndDerivativePerThreadVariables[i].st_Derivative.SetSize(this->GetNumberOfParameters()); m_GetValueAndDerivativePerThreadVariables[i].st_Derivative.Fill(DerivativeValueType{}); } } // end InitializeThreadingParameters() /** * ****************** InitializeLimiters ***************************** */ template void AdvancedImageToImageMetric::InitializeLimiters() { /** Set up fixed limiter. */ if (this->GetUseFixedImageLimiter()) { if (this->GetFixedImageLimiter() == nullptr) { itkExceptionMacro("No fixed image limiter has been set!"); } const auto computeFixedImageExtrema = ComputeImageExtremaFilter::New(); computeFixedImageExtrema->SetInput(this->GetFixedImage()); computeFixedImageExtrema->SetImageSpatialMask(this->GetFixedImageMask()); computeFixedImageExtrema->Update(); m_FixedImageTrueMax = computeFixedImageExtrema->GetMaximum(); m_FixedImageTrueMin = computeFixedImageExtrema->GetMinimum(); m_FixedImageMinLimit = static_cast( m_FixedImageTrueMin - m_FixedLimitRangeRatio * (m_FixedImageTrueMax - m_FixedImageTrueMin)); m_FixedImageMaxLimit = static_cast( m_FixedImageTrueMax + m_FixedLimitRangeRatio * (m_FixedImageTrueMax - m_FixedImageTrueMin)); m_FixedImageLimiter->SetLowerThreshold(static_cast(m_FixedImageTrueMin)); m_FixedImageLimiter->SetUpperThreshold(static_cast(m_FixedImageTrueMax)); m_FixedImageLimiter->SetLowerBound(m_FixedImageMinLimit); m_FixedImageLimiter->SetUpperBound(m_FixedImageMaxLimit); m_FixedImageLimiter->Initialize(); } /** Set up moving limiter. */ if (this->GetUseMovingImageLimiter()) { if (this->GetMovingImageLimiter() == nullptr) { itkExceptionMacro("No moving image limiter has been set!"); } const auto computeMovingImageExtrema = ComputeImageExtremaFilter::New(); computeMovingImageExtrema->SetInput(this->GetMovingImage()); computeMovingImageExtrema->SetImageSpatialMask(this->GetMovingImageMask()); computeMovingImageExtrema->Update(); m_MovingImageTrueMax = computeMovingImageExtrema->GetMaximum(); m_MovingImageTrueMin = computeMovingImageExtrema->GetMinimum(); m_MovingImageMinLimit = static_cast( m_MovingImageTrueMin - m_MovingLimitRangeRatio * (m_MovingImageTrueMax - m_MovingImageTrueMin)); m_MovingImageMaxLimit = static_cast( m_MovingImageTrueMax + m_MovingLimitRangeRatio * (m_MovingImageTrueMax - m_MovingImageTrueMin)); m_MovingImageLimiter->SetLowerThreshold(static_cast(m_MovingImageTrueMin)); m_MovingImageLimiter->SetUpperThreshold(static_cast(m_MovingImageTrueMax)); m_MovingImageLimiter->SetLowerBound(m_MovingImageMinLimit); m_MovingImageLimiter->SetUpperBound(m_MovingImageMaxLimit); m_MovingImageLimiter->Initialize(); } } // end InitializeLimiters() /** * ********************* InitializeImageSampler **************************** */ template void AdvancedImageToImageMetric::InitializeImageSampler() { if (this->GetUseImageSampler()) { /** Check if the ImageSampler is set. */ if (!m_ImageSampler) { itkExceptionMacro("ImageSampler is not present"); } /** Initialize the Image Sampler. */ m_ImageSampler->SetInput(Superclass::m_FixedImage); m_ImageSampler->SetMask(this->GetFixedImageMask()); m_ImageSampler->SetInputImageRegion(this->GetFixedImageRegion()); } } // end InitializeImageSampler() /** * ****************** CheckForBSplineInterpolator ********************** */ template void AdvancedImageToImageMetric::CheckForBSplineInterpolator() { /** Check if the interpolator is of type BSplineInterpolateImageFunction, * or of type AdvancedLinearInterpolateImageFunction. * If so, we can make use of their EvaluateDerivatives methods. * Otherwise, we precompute the gradients using a central difference scheme, * and do evaluate the gradient using nearest neighbor interpolation. */ InterpolatorType * const interpolator = Superclass::m_Interpolator.GetPointer(); m_BSplineInterpolator = dynamic_cast(interpolator); if (m_BSplineInterpolator) { itkDebugMacro("Interpolator is B-spline"); } else { itkDebugMacro("Interpolator is not B-spline"); } m_BSplineInterpolatorFloat = dynamic_cast(interpolator); if (m_BSplineInterpolatorFloat) { itkDebugMacro("Interpolator is BSplineFloat"); } else { itkDebugMacro("Interpolator is not BSplineFloat"); } m_ReducedBSplineInterpolator = dynamic_cast(interpolator); if (m_ReducedBSplineInterpolator) { itkDebugMacro("Interpolator is ReducedBSpline"); } else { itkDebugMacro("Interpolator is not ReducedBSpline"); } m_LinearInterpolator = dynamic_cast(interpolator); /** Don't overwrite the gradient image if m_ComputeGradient == true. * Otherwise we can use a forward difference derivative, or the derivative * provided by the B-spline interpolator. */ if (!Superclass::m_ComputeGradient) { /** In addition, don't compute the moving image gradient for 2D/3D registration, * i.e. whenever the interpolator is a ray cast interpolator. * This is a bit of a hack that does not respect the setting of the boolean * m_ComputeGradient. By doing this, there is no way to ask no gradient * computation at all (to save memory). * The best solution would be to remove everything below this point, and to * override the ComputeGradient() function of ITK by computing a central * difference derivative. This way SetComputeGradient will enable or disable * the gradient computation and let derived classes choose if it needs the * precomputation of the gradient. * * For more details see the post about "2D/3D registration memory issue" in * elastix's mailing list (2 July 2012). */ using NearestNeighborInterpolatorType = NearestNeighborInterpolateImageFunction; if (dynamic_cast(interpolator)) { using CentralDifferenceGradientFilterType = GradientImageFilter; elastix::DefaultConstruct centralDifferenceGradientFilter{}; centralDifferenceGradientFilter.SetUseImageSpacing(true); centralDifferenceGradientFilter.SetInput(Superclass::m_MovingImage); centralDifferenceGradientFilter.Update(); Superclass::m_GradientImage = centralDifferenceGradientFilter.GetOutput(); } else { using RayCastInterpolatorType = itk::AdvancedRayCastInterpolateImageFunction; assert(m_BSplineInterpolator || m_BSplineInterpolatorFloat || m_ReducedBSplineInterpolator || m_LinearInterpolator || dynamic_cast(interpolator)); Superclass::m_GradientImage = nullptr; } } } // end CheckForBSplineInterpolator() /** * ****************** CheckForAdvancedTransform ********************** */ template void AdvancedImageToImageMetric::CheckForAdvancedTransform() { /** Check if the transform is of type AdvancedTransform. */ m_AdvancedTransform = dynamic_cast(Superclass::m_Transform.GetPointer()); if (!m_AdvancedTransform) { itkDebugMacro("Transform is not Advanced"); itkExceptionMacro("The AdvancedImageToImageMetric requires an AdvancedTransform"); } else { itkDebugMacro("Transform is Advanced"); } } // end CheckForAdvancedTransform() /** * ****************** CheckForBSplineTransform ********************** */ template void AdvancedImageToImageMetric::CheckForBSplineTransform() const { /** Check if this transform is a combo transform. */ CombinationTransformType * testPtr_combo = dynamic_cast(m_AdvancedTransform.GetPointer()); /** Check if this transform is a B-spline transform. */ BSplineOrder1TransformType * testPtr_1 = dynamic_cast(m_AdvancedTransform.GetPointer()); BSplineOrder2TransformType * testPtr_2 = dynamic_cast(m_AdvancedTransform.GetPointer()); BSplineOrder3TransformType * testPtr_3 = dynamic_cast(m_AdvancedTransform.GetPointer()); bool transformIsBSpline = false; if (testPtr_1 || testPtr_2 || testPtr_3) { transformIsBSpline = true; } else if (testPtr_combo) { /** Check if the current transform is a B-spline transform. */ const BSplineOrder1TransformType * testPtr_1b = dynamic_cast(testPtr_combo->GetCurrentTransform()); const BSplineOrder2TransformType * testPtr_2b = dynamic_cast(testPtr_combo->GetCurrentTransform()); const BSplineOrder3TransformType * testPtr_3b = dynamic_cast(testPtr_combo->GetCurrentTransform()); if (testPtr_1b || testPtr_2b || testPtr_3b) { transformIsBSpline = true; } } /** Store the result. */ m_TransformIsBSpline = transformIsBSpline; } // end CheckForBSplineTransform() /** * ******************* EvaluateMovingImageValueAndDerivativeWithOptionalThreadId ****************** */ template template bool AdvancedImageToImageMetric::EvaluateMovingImageValueAndDerivativeWithOptionalThreadId( const MovingImagePointType & mappedPoint, RealType & movingImageValue, MovingImageDerivativeType * gradient, const TOptionalThreadId... optionalThreadId) const { /** Check if mapped point inside image buffer. */ MovingImageContinuousIndexType cindex; Superclass::m_Interpolator->ConvertPointToContinuousIndex(mappedPoint, cindex); bool sampleOk = Superclass::m_Interpolator->IsInsideBuffer(cindex); if (sampleOk) { /** Compute value and possibly derivative. */ if (gradient) { if (m_BSplineInterpolator && !Superclass::m_ComputeGradient) { /** Compute moving image value and gradient using the B-spline kernel. */ m_BSplineInterpolator->EvaluateValueAndDerivativeAtContinuousIndex( cindex, movingImageValue, *gradient, optionalThreadId...); } else if (m_BSplineInterpolatorFloat && !Superclass::m_ComputeGradient) { /** Compute moving image value and gradient using the B-spline kernel. */ m_BSplineInterpolatorFloat->EvaluateValueAndDerivativeAtContinuousIndex( cindex, movingImageValue, *gradient, optionalThreadId...); } else if (m_ReducedBSplineInterpolator && !Superclass::m_ComputeGradient) { /** Compute moving image value and gradient using the B-spline kernel. */ movingImageValue = Superclass::m_Interpolator->EvaluateAtContinuousIndex(cindex); (*gradient) = m_ReducedBSplineInterpolator->EvaluateDerivativeAtContinuousIndex(cindex); // m_ReducedBSplineInterpolator->EvaluateValueAndDerivativeAtContinuousIndex( // cindex, movingImageValue, *gradient ); } else if (m_LinearInterpolator && !Superclass::m_ComputeGradient) { /** Compute moving image value and gradient using the linear interpolator. */ m_LinearInterpolator->EvaluateValueAndDerivativeAtContinuousIndex(cindex, movingImageValue, *gradient); } else { /** Get the gradient by NearestNeighboorInterpolation of the gradient image. * It is assumed that the gradient image is computed. */ movingImageValue = Superclass::m_Interpolator->EvaluateAtContinuousIndex(cindex); MovingImageIndexType index; for (unsigned int j = 0; j < MovingImageDimension; ++j) { index[j] = static_cast(Math::Round(cindex[j])); } (*gradient) = Superclass::m_GradientImage->GetPixel(index); } /** The moving image gradient is multiplied with its scales, when requested. */ if (m_UseMovingImageDerivativeScales) { if (!m_ScaleGradientWithRespectToMovingImageOrientation) { for (unsigned int i = 0; i < MovingImageDimension; ++i) { (*gradient)[i] *= m_MovingImageDerivativeScales[i]; } } else { /** Optionally, the scales are applied with respect to the moving image orientation. * The above default option implicitly applies the scales with respect to the * orientation of the transformation axis. In some cases you may want to restrict * moving image motion with respect to its own axes. This is achieved below by pre * and post rotation by the direction cosines of the moving image. * First the gradient is rotated backwards to a standardized axis. */ using InternalMatrixType = typename MovingImageType::DirectionType::InternalMatrixType; const InternalMatrixType M = this->GetMovingImage()->GetDirection().GetVnlMatrix(); vnl_vector rotated_gradient_vnl = M.transpose() * gradient->GetVnlVector(); /** Then scales are applied. */ for (unsigned int i = 0; i < MovingImageDimension; ++i) { rotated_gradient_vnl[i] *= m_MovingImageDerivativeScales[i]; } /** The scaled gradient is then rotated forwards again. */ rotated_gradient_vnl = M * rotated_gradient_vnl; /** Copy the vnl version back to the original. */ for (unsigned int i = 0; i < MovingImageDimension; ++i) { (*gradient)[i] = rotated_gradient_vnl[i]; } } } // end if m_UseMovingImageDerivativeScales } // end if gradient else { movingImageValue = Superclass::m_Interpolator->EvaluateAtContinuousIndex(cindex); } } // end if sampleOk return sampleOk; } // end EvaluateMovingImageValueAndDerivativeWithOptionalThreadId() /** * *************** EvaluateTransformJacobianInnerProduct **************** */ template void AdvancedImageToImageMetric::EvaluateTransformJacobianInnerProduct( const TransformJacobianType & jacobian, const MovingImageDerivativeType & movingImageDerivative, DerivativeType & imageJacobian) const { /** Multiple the 1-by-dim vector movingImageDerivative with the * dim-by-length matrix jacobian, to get a 1-by-length vector imageJacobian. * An optimized route can be taken for B-spline transforms. */ if (m_TransformIsBSpline) { // For the B-spline we know that the Jacobian is mostly empty. // [ j ... j 0 ... 0 0 ... 0 ] // jac = [ 0 ... 0 j ... j 0 ... 0 ] // [ 0 ... 0 0 ... 0 j ... j ] const unsigned int sizeImageJacobian = imageJacobian.GetSize(); const unsigned int numberOfParametersPerDimension = sizeImageJacobian / FixedImageDimension; unsigned int counter = 0; for (unsigned int dim = 0; dim < FixedImageDimension; ++dim) { const double imDeriv = movingImageDerivative[dim]; for (unsigned int mu = 0; mu < numberOfParametersPerDimension; ++mu) { imageJacobian(counter) = jacobian(dim, counter) * imDeriv; ++counter; } } } else { /** Otherwise perform a full multiplication. */ ImplementationDetails::EvaluateInnerProduct(jacobian, movingImageDerivative, imageJacobian); } } // end EvaluateTransformJacobianInnerProduct() /** * ********************** TransformPoint ************************ */ template auto AdvancedImageToImageMetric::TransformPoint(const FixedImagePointType & fixedImagePoint) const -> MovingImagePointType { return Superclass::m_Transform->TransformPoint(fixedImagePoint); } // end TransformPoint() /** * *************** EvaluateTransformJacobian **************** */ template bool AdvancedImageToImageMetric::EvaluateTransformJacobian( const FixedImagePointType & fixedImagePoint, TransformJacobianType & jacobian, NonZeroJacobianIndicesType & nzji) const { /** Advanced transform: generic sparse Jacobian support */ m_AdvancedTransform->GetJacobian(fixedImagePoint, jacobian, nzji); /** For future use: return whether the sample is valid */ const bool valid = true; return valid; } // end EvaluateTransformJacobian() /** * ************************** IsInsideMovingMask ************************* */ template bool AdvancedImageToImageMetric::IsInsideMovingMask(const MovingImagePointType & point) const { /** If a mask has been set: */ if (const auto * const mask = this->GetMovingImageMask()) { return mask->IsInsideInWorldSpace(point); } /** If no mask has been set, just return true. */ return true; } // end IsInsideMovingMask() /** * *********************** BeforeThreadedGetValueAndDerivative *********************** */ template void AdvancedImageToImageMetric::BeforeThreadedGetValueAndDerivative( const TransformParametersType & parameters) const { /** In this function do all stuff that cannot be multi-threaded. */ if (m_UseMetricSingleThreaded) { this->SetTransformParameters(parameters); if (m_UseImageSampler) { this->GetImageSampler()->Update(); } } } // end BeforeThreadedGetValueAndDerivative() /** * **************** GetValueThreaderCallback ******* */ template ITK_THREAD_RETURN_TYPE AdvancedImageToImageMetric::GetValueThreaderCallback(void * arg) { assert(arg); const auto & infoStruct = *static_cast(arg); ThreadIdType threadID = infoStruct.WorkUnitID; assert(infoStruct.UserData); const auto & userData = *static_cast(infoStruct.UserData); assert(userData.st_Metric); const Self & metric = *(userData.st_Metric); metric.ThreadedGetValue(threadID); return ITK_THREAD_RETURN_DEFAULT_VALUE; } // end GetValueThreaderCallback() /** * *********************** LaunchGetValueThreaderCallback*************** */ template void AdvancedImageToImageMetric::LaunchGetValueThreaderCallback() const { /** Setup threader and launch. */ Superclass::m_Threader->SetSingleMethodAndExecute(this->GetValueThreaderCallback, &m_ThreaderMetricParameters); } // end LaunchGetValueThreaderCallback() /** * **************** GetValueAndDerivativeThreaderCallback ******* */ template ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION AdvancedImageToImageMetric::GetValueAndDerivativeThreaderCallback(void * arg) { assert(arg); const auto & infoStruct = *static_cast(arg); ThreadIdType threadID = infoStruct.WorkUnitID; assert(infoStruct.UserData); const auto & userData = *static_cast(infoStruct.UserData); assert(userData.st_Metric); const Self & metric = *(userData.st_Metric); metric.ThreadedGetValueAndDerivative(threadID); return ITK_THREAD_RETURN_DEFAULT_VALUE; } // end GetValueAndDerivativeThreaderCallback() /** * *********************** LaunchGetValueAndDerivativeThreaderCallback*************** */ template void AdvancedImageToImageMetric::LaunchGetValueAndDerivativeThreaderCallback() const { /** Setup threader and launch. */ Superclass::m_Threader->SetSingleMethodAndExecute(this->GetValueAndDerivativeThreaderCallback, &m_ThreaderMetricParameters); } // end LaunchGetValueAndDerivativeThreaderCallback() /** *********** AccumulateDerivativesThreaderCallback ************* */ template ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION AdvancedImageToImageMetric::AccumulateDerivativesThreaderCallback(void * arg) { assert(arg); const auto & infoStruct = *static_cast(arg); ThreadIdType threadID = infoStruct.WorkUnitID; ThreadIdType nrOfThreads = infoStruct.NumberOfWorkUnits; assert(infoStruct.UserData); const auto & userData = *static_cast(infoStruct.UserData); assert(userData.st_Metric); Self & metric = *(userData.st_Metric); const unsigned int numPar = metric.GetNumberOfParameters(); const unsigned int subSize = static_cast(std::ceil(static_cast(numPar) / static_cast(nrOfThreads))); const unsigned int jmin = threadID * subSize; const unsigned int jmax = std::min((threadID + 1) * subSize, numPar); /** This thread accumulates all sub-derivatives into a single one, for the * range [ jmin, jmax [. Additionally, the sub-derivatives are reset. */ const DerivativeValueType normalization = 1.0 / userData.st_NormalizationFactor; for (unsigned int j = jmin; j < jmax; ++j) { DerivativeValueType sum{}; for (ThreadIdType i = 0; i < nrOfThreads; ++i) { sum += metric.m_GetValueAndDerivativePerThreadVariables[i].st_Derivative[j]; /** Reset this variable for the next iteration. */ metric.m_GetValueAndDerivativePerThreadVariables[i].st_Derivative[j] = 0.0; } userData.st_DerivativePointer[j] = sum * normalization; } return ITK_THREAD_RETURN_DEFAULT_VALUE; } // end AccumulateDerivativesThreaderCallback() /** * *********************** CheckNumberOfSamples *********************** */ template void AdvancedImageToImageMetric::CheckNumberOfSamples(unsigned long wanted, unsigned long found) const { Superclass::m_NumberOfPixelsCounted = found; if (found < wanted * this->GetRequiredRatioOfValidSamples()) { itkExceptionMacro("Too many samples map outside moving image buffer: " << found << " / " << wanted << '\n'); } } // end CheckNumberOfSamples() /** * ********************* PrintSelf **************************** */ template void AdvancedImageToImageMetric::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); /** Variables related to the Sampler. */ os << indent << "Variables related to the Sampler: " << std::endl; os << indent.GetNextIndent() << "ImageSampler: " << m_ImageSampler.GetPointer() << std::endl; os << indent.GetNextIndent() << "UseImageSampler: " << m_UseImageSampler << std::endl; /** Variables for the Limiters. */ os << indent << "Variables related to the Limiters: " << std::endl; os << indent.GetNextIndent() << "FixedLimitRangeRatio: " << m_FixedLimitRangeRatio << std::endl; os << indent.GetNextIndent() << "MovingLimitRangeRatio: " << m_MovingLimitRangeRatio << std::endl; os << indent.GetNextIndent() << "UseFixedImageLimiter: " << m_UseFixedImageLimiter << std::endl; os << indent.GetNextIndent() << "UseMovingImageLimiter: " << m_UseMovingImageLimiter << std::endl; os << indent.GetNextIndent() << "FixedImageLimiter: " << m_FixedImageLimiter.GetPointer() << std::endl; os << indent.GetNextIndent() << "MovingImageLimiter: " << m_MovingImageLimiter.GetPointer() << std::endl; os << indent.GetNextIndent() << "FixedImageTrueMin: " << m_FixedImageTrueMin << std::endl; os << indent.GetNextIndent() << "MovingImageTrueMin: " << m_MovingImageTrueMin << std::endl; os << indent.GetNextIndent() << "FixedImageTrueMax: " << m_FixedImageTrueMax << std::endl; os << indent.GetNextIndent() << "MovingImageTrueMax: " << m_MovingImageTrueMax << std::endl; os << indent.GetNextIndent() << "FixedImageMinLimit: " << m_FixedImageMinLimit << std::endl; os << indent.GetNextIndent() << "MovingImageMinLimit: " << m_MovingImageMinLimit << std::endl; os << indent.GetNextIndent() << "FixedImageMaxLimit: " << m_FixedImageMaxLimit << std::endl; os << indent.GetNextIndent() << "MovingImageMaxLimit: " << m_MovingImageMaxLimit << std::endl; /** Variables related to image derivative computation. */ os << indent << "Variables related to image derivative computation: " << std::endl; os << indent.GetNextIndent() << "BSplineInterpolator: " << m_BSplineInterpolator.GetPointer() << std::endl; os << indent.GetNextIndent() << "BSplineInterpolatorFloat: " << m_BSplineInterpolatorFloat.GetPointer() << std::endl; /** Variables used when the transform is a B-spline transform. */ os << indent << "Variables store the transform as an AdvancedTransform: " << std::endl; os << indent.GetNextIndent() << "AdvancedTransform: " << m_AdvancedTransform.GetPointer() << std::endl; /** Other variables. */ os << indent << "Other variables of the AdvancedImageToImageMetric: " << std::endl; os << indent.GetNextIndent() << "RequiredRatioOfValidSamples: " << m_RequiredRatioOfValidSamples << std::endl; os << indent.GetNextIndent() << "UseMovingImageDerivativeScales: " << m_UseMovingImageDerivativeScales << std::endl; os << indent.GetNextIndent() << "MovingImageDerivativeScales: " << m_MovingImageDerivativeScales << std::endl; } // end PrintSelf() } // end namespace itk #endif // end #ifndef _itkAdvancedImageToImageMetric_hxx elastix-5.2.0/Common/CostFunctions/itkExponentialLimiterFunction.h000066400000000000000000000060741474534065100254430ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkExponentialLimiterFunction_h #define itkExponentialLimiterFunction_h #include "itkLimiterFunctionBase.h" namespace itk { /** * \class ExponentialLimiterFunction * \brief A soft limiter * * If the input value exceeds the upper/lower threshold the output is * diminished/increased, such that it never will exceed the UpperBound/LowerBound. * It does this in a smooth manner, with an exponential function. * * \f[ L(f(x)) = (T-B) e^{(f-T)/(T-B)} + B, \f] * where \f$B\f$ is the upper/lower bound and \f$T\f$ the upper/lower threshold * * \ingroup Functions * \sa LimiterFunctionBase, HardLimiterFunction * */ template class ITK_TEMPLATE_EXPORT ExponentialLimiterFunction : public LimiterFunctionBase { public: ITK_DISALLOW_COPY_AND_MOVE(ExponentialLimiterFunction); /** Standard class typedefs. */ using Self = ExponentialLimiterFunction; using Superclass = LimiterFunctionBase; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Run-time type information (and related methods). */ itkTypeMacro(ExponentialLimiterFunction, LimiterFunctionBase); /** Define the New() function, for creation via the ObjectFactory */ itkNewMacro(Self); /** Superclass' static consts */ itkStaticConstMacro(Dimension, unsigned int, Superclass::Dimension); /** Superclass' typedefs */ using typename Superclass::InputType; using typename Superclass::OutputType; using typename Superclass::DerivativeValueType; using typename Superclass::DerivativeType; /** Limit the input value */ OutputType Evaluate(const InputType & input) const override; /** Limit the input value and change the input function derivative accordingly */ OutputType Evaluate(const InputType & input, DerivativeType & derivative) const override; /** Initialize the limiter; calls the ComputeLimiterSettings() function */ void Initialize() override; protected: ExponentialLimiterFunction(); ~ExponentialLimiterFunction() override = default; virtual void ComputeLimiterSettings(); double m_UTminUB{}; double m_UTminUBinv{}; double m_LTminLB{}; double m_LTminLBinv{}; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkExponentialLimiterFunction.hxx" #endif #endif elastix-5.2.0/Common/CostFunctions/itkExponentialLimiterFunction.hxx000066400000000000000000000110321474534065100260110ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkExponentialLimiterFunction_hxx #define itkExponentialLimiterFunction_hxx #include "itkExponentialLimiterFunction.h" #include namespace itk { /** * *************** Constructor ******************** */ template ExponentialLimiterFunction::ExponentialLimiterFunction() { this->ComputeLimiterSettings(); } // end Constructor /** * **************** Initialize *********************** */ template void ExponentialLimiterFunction::Initialize() { this->ComputeLimiterSettings(); } // end Initialize() /** * ******************** Evaluate *********************** */ template auto ExponentialLimiterFunction::Evaluate(const InputType & input) const -> OutputType { /** Apply a soft limit if the input is larger than the UpperThreshold */ const double diffU = static_cast(input - this->m_UpperThreshold); if (diffU > 1e-10) { return static_cast(this->m_UTminUB * std::exp(this->m_UTminUBinv * diffU) + this->m_UpperBound); } /** Apply a soft limit if the input is smaller than the LowerThreshold */ const double diffL = static_cast(input - this->m_LowerThreshold); if (diffL < -1e-10) { return static_cast(this->m_LTminLB * std::exp(this->m_LTminLBinv * diffL) + this->m_LowerBound); } /** Leave the value as it is */ return static_cast(input); } // end Evaluate() /** * *********************** Evaluate ************************* */ template auto ExponentialLimiterFunction::Evaluate(const InputType & input, DerivativeType & derivative) const -> OutputType { /** Apply a soft limit if the input is larger than the UpperThreshold */ const double diffU = static_cast(input - this->m_UpperThreshold); if (diffU > 1e-10) { const double temp = this->m_UTminUB * std::exp(this->m_UTminUBinv * diffU); const double gradientfactor = this->m_UTminUBinv * temp; for (unsigned int i = 0; i < Dimension; ++i) { derivative[i] = static_cast(derivative[i] * gradientfactor); } return static_cast(temp + this->m_UpperBound); } /** Apply a soft limit if the input is smaller than the LowerThreshold */ const double diffL = static_cast(input - this->m_LowerThreshold); if (diffL < -1e-10) { const double temp = this->m_LTminLB * std::exp(this->m_LTminLBinv * diffL); const double gradientfactor = this->m_LTminLBinv * temp; for (unsigned int i = 0; i < Dimension; ++i) { derivative[i] = static_cast(derivative[i] * gradientfactor); } return static_cast(temp + this->m_LowerBound); } /** Leave the value and derivative as they are */ return static_cast(input); } // end Evaluate() /** * ******************** ComputeLimiterSettings ******************** */ template void ExponentialLimiterFunction::ComputeLimiterSettings() { this->m_UTminUB = static_cast(this->m_UpperThreshold) - this->m_UpperBound; this->m_LTminLB = static_cast(this->m_LowerThreshold) - this->m_LowerBound; if (this->m_UTminUB < -1e-10) { this->m_UTminUBinv = 1.0 / this->m_UTminUB; } else { /** The result is a hard limiter */ this->m_UTminUB = 0.0; this->m_UTminUBinv = 0.0; } if (this->m_LTminLB > 1e-10) { this->m_LTminLBinv = 1.0 / this->m_LTminLB; } else { /** The result is a hard limiter */ this->m_LTminLB = 0.0; this->m_LTminLBinv = 0.0; } } // end ComputeLimiterSettings() } // end namespace itk #endif elastix-5.2.0/Common/CostFunctions/itkHardLimiterFunction.h000066400000000000000000000050731474534065100240310ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkHardLimiterFunction_h #define itkHardLimiterFunction_h #include "itkLimiterFunctionBase.h" namespace itk { /** * \class HardLimiterFunction * \brief A hard limiter * * If the input value exceeds the upper/lower bound the output is * set to the upper/lower bound and the derivative is filled with zeros. * * \ingroup Functions * \sa LimiterFunctionBase, ExponentialLimiterFunction * */ template class ITK_TEMPLATE_EXPORT HardLimiterFunction : public LimiterFunctionBase { public: ITK_DISALLOW_COPY_AND_MOVE(HardLimiterFunction); /** Standard class typedefs. */ using Self = HardLimiterFunction; using Superclass = LimiterFunctionBase; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Run-time type information (and related methods). */ itkTypeMacro(HardLimiterFunction, LimiterFunctionBase); /** Define the New() function, for creation via the ObjectFactory */ itkNewMacro(Self); /** Superclass' static consts */ itkStaticConstMacro(Dimension, unsigned int, Superclass::Dimension); /** Superclass' typedefs */ using typename Superclass::InputType; using typename Superclass::OutputType; using typename Superclass::DerivativeValueType; using typename Superclass::DerivativeType; /** Limit the input value */ OutputType Evaluate(const InputType & input) const override; /** Limit the input value and change the input function derivative accordingly */ OutputType Evaluate(const InputType & input, DerivativeType & derivative) const override; protected: HardLimiterFunction() = default; ~HardLimiterFunction() override = default; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkHardLimiterFunction.hxx" #endif #endif elastix-5.2.0/Common/CostFunctions/itkHardLimiterFunction.hxx000066400000000000000000000033031474534065100244030ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkHardLimiterFunction_hxx #define itkHardLimiterFunction_hxx #include "itkHardLimiterFunction.h" #include namespace itk { template auto HardLimiterFunction::Evaluate(const InputType & input) const -> OutputType { OutputType output = std::min(static_cast(input), this->m_UpperBound); return (std::max(output, this->m_LowerBound)); } // end Evaluate() template auto HardLimiterFunction::Evaluate(const InputType & input, DerivativeType & derivative) const -> OutputType { if (input > this->m_UpperBound) { derivative.Fill(OutputType{}); return (this->m_UpperBound); } if (input < this->m_LowerBound) { derivative.Fill(OutputType{}); return (this->m_LowerBound); } return (static_cast(input)); } // end Evaluate() } // end namespace itk #endif elastix-5.2.0/Common/CostFunctions/itkImageToImageMetricWithFeatures.h000066400000000000000000000224641474534065100261110ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkImageToImageMetricWithFeatures_h #define itkImageToImageMetricWithFeatures_h #include "itkAdvancedImageToImageMetric.h" #include "itkInterpolateImageFunction.h" namespace itk { /** \class ImageToImageMetricWithFeatures * \brief Computes similarity between regions of two images. * * This base class adds functionality that makes it possible * to use fixed and moving image features. * * \ingroup RegistrationMetrics * */ template class ITK_TEMPLATE_EXPORT ImageToImageMetricWithFeatures : public AdvancedImageToImageMetric { public: ITK_DISALLOW_COPY_AND_MOVE(ImageToImageMetricWithFeatures); /** Standard class typedefs. */ using Self = ImageToImageMetricWithFeatures; using Superclass = AdvancedImageToImageMetric; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Run-time type information (and related methods). */ itkTypeMacro(ImageToImageMetricWithFeatures, AdvancedImageToImageMetric); /** Typedefs from the superclass. */ using typename Superclass::CoordinateRepresentationType; using typename Superclass::MovingImageType; using typename Superclass::MovingImagePixelType; using typename Superclass::MovingImageConstPointer; using typename Superclass::FixedImageType; using typename Superclass::FixedImageConstPointer; using typename Superclass::FixedImageRegionType; using typename Superclass::TransformType; using typename Superclass::TransformPointer; using typename Superclass::InputPointType; using typename Superclass::OutputPointType; using typename Superclass::TransformParametersType; using typename Superclass::TransformJacobianType; using typename Superclass::InterpolatorType; using typename Superclass::InterpolatorPointer; using typename Superclass::RealType; using typename Superclass::GradientPixelType; using typename Superclass::GradientImageType; using typename Superclass::GradientImagePointer; using typename Superclass::FixedImageMaskType; using typename Superclass::FixedImageMaskPointer; using typename Superclass::MovingImageMaskType; using typename Superclass::MovingImageMaskPointer; using typename Superclass::MeasureType; using typename Superclass::DerivativeType; using typename Superclass::ParametersType; using typename Superclass::FixedImagePixelType; using typename Superclass::MovingImageRegionType; using typename Superclass::ImageSamplerType; using typename Superclass::ImageSamplerPointer; using typename Superclass::ImageSampleContainerType; using typename Superclass::ImageSampleContainerPointer; using typename Superclass::InternalMaskPixelType; using typename Superclass::InternalMovingImageMaskType; using typename Superclass::MovingImageMaskInterpolatorType; using typename Superclass::FixedImageLimiterType; using typename Superclass::MovingImageLimiterType; using typename Superclass::FixedImageLimiterOutputType; using typename Superclass::MovingImageLimiterOutputType; /** The fixed image dimension. */ itkStaticConstMacro(FixedImageDimension, unsigned int, FixedImageType::ImageDimension); /** The moving image dimension. */ itkStaticConstMacro(MovingImageDimension, unsigned int, MovingImageType::ImageDimension); /** Typedefs for the feature images. */ using FixedFeatureImageType = TFixedFeatureImage; using FixedFeatureImagePointer = typename FixedFeatureImageType::Pointer; using MovingFeatureImageType = TMovingFeatureImage; using MovingFeatureImagePointer = typename MovingFeatureImageType::Pointer; using FixedFeatureImageVectorType = std::vector; using MovingFeatureImageVectorType = std::vector; /** Typedefs for the feature images interpolators. */ using FixedFeatureInterpolatorType = InterpolateImageFunction; using MovingFeatureInterpolatorType = InterpolateImageFunction; using FixedFeatureInterpolatorPointer = typename FixedFeatureInterpolatorType::Pointer; using MovingFeatureInterpolatorPointer = typename MovingFeatureInterpolatorType::Pointer; using FixedFeatureInterpolatorVectorType = std::vector; using MovingFeatureInterpolatorVectorType = std::vector; /** Set the number of fixed feature images. */ void SetNumberOfFixedFeatureImages(unsigned int arg); /** Get the number of fixed feature images. */ itkGetConstMacro(NumberOfFixedFeatureImages, unsigned int); /** Functions to set the fixed feature images. */ void SetFixedFeatureImage(unsigned int i, FixedFeatureImageType * im); void SetFixedFeatureImage(FixedFeatureImageType * im) { this->SetFixedFeatureImage(0, im); } /** Functions to get the fixed feature images. */ const FixedFeatureImageType * GetFixedFeatureImage(unsigned int i) const; const FixedFeatureImageType * GetFixedFeatureImage() const { return this->GetFixedFeatureImage(0); } /** Functions to set the fixed feature interpolators. */ void SetFixedFeatureInterpolator(unsigned int i, FixedFeatureInterpolatorType * interpolator); void SetFixedFeatureInterpolator(FixedFeatureInterpolatorType * interpolator) { this->SetFixedFeatureInterpolator(0, interpolator); } /** Functions to get the fixed feature interpolators. */ const FixedFeatureInterpolatorType * GetFixedFeatureInterpolator(unsigned int i) const; const FixedFeatureInterpolatorType * GetFixedFeatureInterpolator() const { return this->GetFixedFeatureInterpolator(0); } /** Set the number of moving feature images. */ void SetNumberOfMovingFeatureImages(unsigned int arg); /** Get the number of moving feature images. */ itkGetConstMacro(NumberOfMovingFeatureImages, unsigned int); /** Functions to set the moving feature images. */ void SetMovingFeatureImage(unsigned int i, MovingFeatureImageType * im); void SetMovingFeatureImage(MovingFeatureImageType * im) { this->SetMovingFeatureImage(0, im); } /** Functions to get the moving feature images. */ const MovingFeatureImageType * GetMovingFeatureImage(unsigned int i) const; const MovingFeatureImageType * GetMovingFeatureImage() const { return this->GetMovingFeatureImage(0); } /** Functions to set the moving feature interpolators. */ void SetMovingFeatureInterpolator(unsigned int i, MovingFeatureInterpolatorType * interpolator); void SetMovingFeatureInterpolator(MovingFeatureInterpolatorType * interpolator) { this->SetMovingFeatureInterpolator(0, interpolator); } /** Functions to get the moving feature interpolators. */ const MovingFeatureInterpolatorType * GetMovingFeatureInterpolator(unsigned int i) const; const MovingFeatureInterpolatorType * GetMovingFeatureInterpolator() const { return this->GetMovingFeatureInterpolator(0); } /** Initialize the metric. */ virtual void Initialize(); protected: ImageToImageMetricWithFeatures() = default; virtual ~ImageToImageMetricWithFeatures() {} void PrintSelf(std::ostream & os, Indent indent) const; using typename Superclass::BSplineInterpolatorType; using BSplineInterpolatorPointer = typename BSplineInterpolatorType::Pointer; using BSplineFeatureInterpolatorVectorType = std::vector; using typename Superclass::FixedImagePointType; using typename Superclass::MovingImagePointType; using typename Superclass::MovingImageDerivativeType; using typename Superclass::MovingImageContinuousIndexType; /** Member variables. */ unsigned int m_NumberOfFixedFeatureImages{ 0 }; unsigned int m_NumberOfMovingFeatureImages{ 0 }; FixedFeatureImageVectorType m_FixedFeatureImages{}; MovingFeatureImageVectorType m_MovingFeatureImages{}; FixedFeatureInterpolatorVectorType m_FixedFeatureInterpolators{}; MovingFeatureInterpolatorVectorType m_MovingFeatureInterpolators{}; std::vector m_FeatureInterpolatorsIsBSpline{}; BSplineFeatureInterpolatorVectorType m_MovingFeatureBSplineInterpolators{}; /** Initialize variables for image derivative computation; this * method is called by Initialize. */ virtual void CheckForBSplineFeatureInterpolators(); }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkImageToImageMetricWithFeatures.hxx" #endif #endif // end #ifndef itkImageToImageMetricWithFeatures_h elastix-5.2.0/Common/CostFunctions/itkImageToImageMetricWithFeatures.hxx000066400000000000000000000303561474534065100264700ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef _itkImageToImageMetricWithFeatures_hxx #define _itkImageToImageMetricWithFeatures_hxx #include "itkImageToImageMetricWithFeatures.h" namespace itk { /** * ********************* Initialize ***************************** */ template void ImageToImageMetricWithFeatures::Initialize() { /** Call the superclass. */ this->Superclass::Initialize(); /** Check the fixed stuff. */ for (unsigned int i = 0; i < m_NumberOfFixedFeatureImages; ++i) { /** Check if all the fixed feature images are set. */ if (!this->m_FixedFeatureImages[i]) { itkExceptionMacro("ERROR: fixed feature image " << i << " is not set."); } /** Check if all the fixed feature interpolators are set. */ if (!this->m_FixedFeatureInterpolators[i]) { itkExceptionMacro("ERROR: fixed feature interpolator " << i << " is not set."); } /** Connect the feature image to the interpolator. */ this->m_FixedFeatureInterpolators[i]->SetInputImage(this->m_FixedFeatureImages[i]); } /** Check the moving stuff. */ for (unsigned int i = 0; i < m_NumberOfMovingFeatureImages; ++i) { /** Check if all the moving feature images are set. */ if (!this->m_MovingFeatureImages[i]) { itkExceptionMacro("ERROR: moving feature image " << i << " is not set."); } /** Check if all the moving feature interpolators are set. */ if (!this->m_MovingFeatureInterpolators[i]) { itkExceptionMacro("ERROR: moving feature interpolator " << i << " is not set."); } /** Connect the feature image to the interpolator. */ this->m_MovingFeatureInterpolators[i]->SetInputImage(this->m_MovingFeatureImages[i]); } /** Check if the moving feature image interpolators are B-spline interpolators. */ this->CheckForBSplineFeatureInterpolators(); } // end Initialize() /** * ********************* SetNumberOfFixedFeatureImages **************************** */ template void ImageToImageMetricWithFeatures:: SetNumberOfFixedFeatureImages(unsigned int arg) { if (this->m_NumberOfFixedFeatureImages != arg) { this->m_FixedFeatureImages.resize(arg); this->m_FixedFeatureInterpolators.resize(arg); this->m_NumberOfFixedFeatureImages = arg; this->Modified(); } } // end SetNumberOfFixedFeatureImages() /** * ********************* SetFixedFeatureImage **************************** */ template void ImageToImageMetricWithFeatures:: SetFixedFeatureImage(unsigned int i, FixedFeatureImageType * im) { if (i + 1 > this->m_NumberOfFixedFeatureImages) { this->m_FixedFeatureImages.resize(i + 1); this->m_FixedFeatureImages[i] = im; this->m_NumberOfFixedFeatureImages = i; this->Modified(); } else { if (this->m_FixedFeatureImages[i] != im) { this->m_FixedFeatureImages[i] = im; this->Modified(); } } } // end SetFixedFeatureImage() /** * ********************* GetFixedFeatureImage **************************** */ template const typename ImageToImageMetricWithFeatures:: FixedFeatureImageType * ImageToImageMetricWithFeatures:: GetFixedFeatureImage(unsigned int i) const { return this->m_FixedFeatureImages[i].GetPointer(); } // end GetFixedFeatureImage() /** * ********************* SetFixedFeatureInterpolator **************************** */ template void ImageToImageMetricWithFeatures:: SetFixedFeatureInterpolator(unsigned int i, FixedFeatureInterpolatorType * interpolator) { if (i + 1 > this->m_NumberOfFixedFeatureImages) { this->m_FixedFeatureInterpolators.resize(i + 1); this->m_FixedFeatureInterpolators[i] = interpolator; this->m_NumberOfFixedFeatureImages = i; this->Modified(); } else { if (this->m_FixedFeatureInterpolators[i] != interpolator) { this->m_FixedFeatureInterpolators[i] = interpolator; this->Modified(); } } } // end SetFixedFeatureInterpolator() /** * ********************* GetFixedFeatureInterpolator **************************** */ template const typename ImageToImageMetricWithFeatures:: FixedFeatureInterpolatorType * ImageToImageMetricWithFeatures:: GetFixedFeatureInterpolator(unsigned int i) const { return this->m_FixedFeatureInterpolators[i].GetPointer(); } // end GetFixedFeatureInterpolator() /** * ********************* SetNumberOfMovingFeatureImages **************************** */ template void ImageToImageMetricWithFeatures:: SetNumberOfMovingFeatureImages(unsigned int arg) { if (this->m_NumberOfMovingFeatureImages != arg) { this->m_MovingFeatureImages.resize(arg); this->m_MovingFeatureInterpolators.resize(arg); this->m_NumberOfMovingFeatureImages = arg; this->Modified(); } } // end SetNumberOfMovingFeatureImages() /** * ********************* SetMovingFeatureImage **************************** */ template void ImageToImageMetricWithFeatures:: SetMovingFeatureImage(unsigned int i, MovingFeatureImageType * im) { if (i + 1 > this->m_NumberOfMovingFeatureImages) { this->m_MovingFeatureImages.resize(i + 1); this->m_MovingFeatureImages[i] = im; this->m_NumberOfMovingFeatureImages = i; this->Modified(); } else { if (this->m_MovingFeatureImages[i] != im) { this->m_MovingFeatureImages[i] = im; this->Modified(); } } } // end SetMovingFeatureImage() /** * ********************* GetMovingFeatureImage **************************** */ template const typename ImageToImageMetricWithFeatures:: MovingFeatureImageType * ImageToImageMetricWithFeatures:: GetMovingFeatureImage(unsigned int i) const { return this->m_MovingFeatureImages[i].GetPointer(); } // end GetMovingFeatureImage() /** * ********************* SetMovingFeatureInterpolator **************************** */ template void ImageToImageMetricWithFeatures:: SetMovingFeatureInterpolator(unsigned int i, MovingFeatureInterpolatorType * interpolator) { if (i + 1 > this->m_NumberOfMovingFeatureImages) { this->m_MovingFeatureInterpolators.resize(i + 1); this->m_MovingFeatureInterpolators[i] = interpolator; this->m_NumberOfMovingFeatureImages = i; this->Modified(); } else { if (this->m_MovingFeatureInterpolators[i] != interpolator) { this->m_MovingFeatureInterpolators[i] = interpolator; this->Modified(); } } } // end SetMovingFeatureInterpolator() /** * ********************* GetMovingFeatureInterpolator **************************** */ template const typename ImageToImageMetricWithFeatures:: MovingFeatureInterpolatorType * ImageToImageMetricWithFeatures:: GetMovingFeatureInterpolator(unsigned int i) const { return this->m_MovingFeatureInterpolators[i].GetPointer(); } // end GetMovingFeatureInterpolator() /** * ****************** CheckForBSplineFeatureInterpolators ********************** */ template void ImageToImageMetricWithFeatures:: CheckForBSplineFeatureInterpolators() { /** Check if the interpolators are of type BSplineInterpolateImageFunction. * If so, we can make use of its EvaluateDerivatives method. * Otherwise, an exception is thrown. */ this->m_FeatureInterpolatorsIsBSpline.resize(this->m_NumberOfMovingFeatureImages, false); this->m_MovingFeatureBSplineInterpolators.resize(this->m_NumberOfMovingFeatureImages); for (unsigned int i = 0; i < this->m_NumberOfMovingFeatureImages; ++i) { BSplineInterpolatorType * testPtr = dynamic_cast(this->m_MovingFeatureInterpolators[i].GetPointer()); if (testPtr) { this->m_FeatureInterpolatorsIsBSpline[i] = true; this->m_MovingFeatureBSplineInterpolators[i] = testPtr; itkDebugMacro("Interpolator " << i << " is B-spline."); } else { itkDebugMacro("Interpolator " << i << " is NOT B-spline."); itkExceptionMacro("Interpolator " << i << " is NOT B-spline."); } } // end for-loop } // end CheckForBSplineFeatureInterpolators() /** * ********************* PrintSelf **************************** */ template void ImageToImageMetricWithFeatures::PrintSelf( std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "NumberOfFixedFeatureImages: " << this->m_NumberOfFixedFeatureImages << std::endl; os << indent << "NumberOfMovingFeatureImages: " << this->m_NumberOfMovingFeatureImages << std::endl; /** Print the feature image pointers. */ for (unsigned int i = 0; i < this->m_NumberOfFixedFeatureImages; ++i) { os << indent << "FixedFeatureImages[" << i << "]: " << this->m_FixedFeatureImages[i].GetPointer() << std::endl; } for (unsigned int i = 0; i < this->m_NumberOfMovingFeatureImages; ++i) { os << indent << "MovingFeatureImages[" << i << "]: " << this->m_MovingFeatureImages[i].GetPointer() << std::endl; } /** Print the feature interpolators pointers. */ for (unsigned int i = 0; i < this->m_NumberOfFixedFeatureImages; ++i) { os << indent << "FixedFeatureInterpolators[" << i << "]: " << this->m_FixedFeatureInterpolators[i].GetPointer() << std::endl; } for (unsigned int i = 0; i < this->m_NumberOfMovingFeatureImages; ++i) { os << indent << "MovingFeatureInterpolators[" << i << "]: " << this->m_MovingFeatureInterpolators[i].GetPointer() << std::endl; } } // end PrintSelf() } // end namespace itk #endif // end #ifndef _itkImageToImageMetricWithFeatures_hxx elastix-5.2.0/Common/CostFunctions/itkLimiterFunctionBase.h000066400000000000000000000104131474534065100240170ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkLimiterFunctionBase_h #define itkLimiterFunctionBase_h #include "itkFunctionBase.h" #include "itkMacro.h" namespace itk { /** * \class LimiterFunctionBase * \brief Base class for all ITK limiter function objects * * LimiterFunctionBase is the base class for ITK limiter function objects. * The abstract method Evaluate() should limit a function, i.e. * it should make sure that its output is below a certain value. * The derivative of a function that is limited also changes. * * In formula: * \f[ L(y) = L(f(x)), \f] * where \f$f(x)\f$ is the original function. and \f$L(f(x))\f$ the limited version. * The derivative with respect to \f$x\f$ should satisfy: * \f[ dL/dx = \frac{dL}{df} \cdot \frac{df}{dx} \f] * * Subclasses must override Evaluate(value) and Evaluate(value, derivative) . * * This class is template over the input type and the dimension of \f$x\f$. * * \ingroup Functions * */ template class ITK_TEMPLATE_EXPORT LimiterFunctionBase : public FunctionBase::RealType> { public: ITK_DISALLOW_COPY_AND_MOVE(LimiterFunctionBase); /** Standard class typedefs. */ using Self = LimiterFunctionBase; using Superclass = FunctionBase::RealType>; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Run-time type information (and related methods). */ itkTypeMacro(LimiterFunctionBase, FunctionBase); itkStaticConstMacro(Dimension, unsigned int, NDimension); /** Superclass' typedefs */ using typename Superclass::InputType; using typename Superclass::OutputType; using DerivativeValueType = OutputType; using DerivativeType = CovariantVector; /** Limit the input value. */ OutputType Evaluate(const InputType & input) const override = 0; /** Limit the input value and change the input function derivative accordingly */ virtual OutputType Evaluate(const InputType & input, DerivativeType & derivative) const = 0; /** Set/Get the upper bound that the output should respect. Make sure it is higher * than the lower bound. */ itkSetMacro(UpperBound, OutputType); itkGetConstMacro(UpperBound, OutputType); /** Set/Get the lower bound that the output should respect. Make sure it is lower * than the higher bound. */ itkSetMacro(LowerBound, OutputType); itkGetConstMacro(LowerBound, OutputType); /** Set the point where the limiter starts to work. Only input values above this number * will possibly be affected. Make sure it is <= than the UpperBound. */ itkSetMacro(UpperThreshold, InputType); itkGetConstMacro(UpperThreshold, InputType); /** Set the point where the limiter starts to work. Only input values below this number * will possibly be affected. Make sure it is >= than the LowerBound. */ itkSetMacro(LowerThreshold, InputType); itkGetConstMacro(LowerThreshold, InputType); /** Initialize the limiter */ virtual void Initialize() {} protected: LimiterFunctionBase() { this->m_UpperBound = itk::NumericTraits::One + itk::NumericTraits::One; this->m_LowerBound = OutputType{}; this->m_UpperThreshold = itk::NumericTraits::One; this->m_LowerThreshold = itk::NumericTraits::One; } ~LimiterFunctionBase() override = default; OutputType m_UpperBound{}; OutputType m_LowerBound{}; InputType m_UpperThreshold{}; InputType m_LowerThreshold{}; }; } // end namespace itk #endif elastix-5.2.0/Common/CostFunctions/itkMultiInputImageToImageMetricBase.h000066400000000000000000000354551474534065100264100ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkMultiInputImageToImageMetricBase_h #define itkMultiInputImageToImageMetricBase_h #include "itkAdvancedImageToImageMetric.h" #include /** Macro for setting the number of objects. */ #define itkSetNumberOfMacro(name) \ virtual void SetNumberOf##name##s(const unsigned int _arg) \ { \ if (this->m_NumberOf##name##s != _arg) \ { \ this->m_##name##Vector.resize(_arg); \ this->m_NumberOf##name##s = _arg; \ this->Modified(); \ } \ } // comments for allowing ; after calling the macro namespace itk { /** \class MultiInputImageToImageMetricBase * * \brief Implements a metric base class that takes multiple inputs. * * * \ingroup RegistrationMetrics * */ template class ITK_TEMPLATE_EXPORT MultiInputImageToImageMetricBase : public AdvancedImageToImageMetric { public: ITK_DISALLOW_COPY_AND_MOVE(MultiInputImageToImageMetricBase); /** Standard class typedefs. */ using Self = MultiInputImageToImageMetricBase; using Superclass = AdvancedImageToImageMetric; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Run-time type information (and related methods). */ itkTypeMacro(MultiInputImageToImageMetricBase, AdvancedImageToImageMetric); /** Constants for the image dimensions */ itkStaticConstMacro(MovingImageDimension, unsigned int, TMovingImage::ImageDimension); itkStaticConstMacro(FixedImageDimension, unsigned int, TFixedImage::ImageDimension); /** Typedefs from the superclass. */ using typename Superclass::CoordinateRepresentationType; using typename Superclass::MovingImageType; using typename Superclass::MovingImagePixelType; using typename Superclass::MovingImagePointer; using typename Superclass::MovingImageConstPointer; using typename Superclass::FixedImageType; using typename Superclass::FixedImagePointer; using typename Superclass::FixedImageConstPointer; using typename Superclass::FixedImageRegionType; using typename Superclass::TransformType; using typename Superclass::TransformPointer; using typename Superclass::InputPointType; using typename Superclass::OutputPointType; using typename Superclass::TransformParametersType; using typename Superclass::TransformJacobianType; using typename Superclass::InterpolatorType; using typename Superclass::InterpolatorPointer; using typename Superclass::RealType; using typename Superclass::GradientPixelType; using typename Superclass::GradientImageType; using typename Superclass::GradientImagePointer; using typename Superclass::FixedImageMaskType; using typename Superclass::FixedImageMaskPointer; using typename Superclass::FixedImageMaskConstPointer; using typename Superclass::MovingImageMaskType; using typename Superclass::MovingImageMaskPointer; using typename Superclass::MovingImageMaskConstPointer; using typename Superclass::MeasureType; using typename Superclass::DerivativeType; using typename Superclass::ParametersType; using FixedImageInterpolatorType = InterpolateImageFunction; using FixedImageInterpolatorPointer = typename FixedImageInterpolatorType::Pointer; /** Typedef's for storing multiple inputs. */ using FixedImageVectorType = std::vector; using FixedImageMaskVectorType = std::vector; using FixedImageRegionVectorType = std::vector; using MovingImageVectorType = std::vector; using MovingImageMaskVectorType = std::vector; using InterpolatorVectorType = std::vector; using FixedImageInterpolatorVectorType = std::vector; /** ******************** Fixed images ******************** */ /** Set the fixed images. */ virtual void SetFixedImage(const FixedImageType * _arg, unsigned int pos); /** Set the first fixed image. */ void SetFixedImage(const FixedImageType * _arg) override { this->SetFixedImage(_arg, 0); } /** Get the fixed images. */ virtual const FixedImageType * GetFixedImage(unsigned int pos) const; /** Get the first fixed image. */ const FixedImageType * GetFixedImage() const override { return this->GetFixedImage(0); } /** Set the number of fixed images. */ itkSetNumberOfMacro(FixedImage); /** Get the number of fixed images. */ itkGetConstMacro(NumberOfFixedImages, unsigned int); /** ******************** Fixed image masks ******************** */ /** Set the fixed image masks. */ virtual void SetFixedImageMask(const FixedImageMaskType * _arg, unsigned int pos); /** Set the first fixed image mask. */ void SetFixedImageMask(const FixedImageMaskType * _arg) override { this->SetFixedImageMask(_arg, 0); } /** Get the fixed image masks. */ virtual const FixedImageMaskType * GetFixedImageMask(unsigned int pos) const; /** Get the first fixed image mask. */ const FixedImageMaskType * GetFixedImageMask() const override { return this->GetFixedImageMask(0); } /** Set the number of fixed image masks. */ itkSetNumberOfMacro(FixedImageMask); /** Get the number of fixed image masks. */ itkGetConstMacro(NumberOfFixedImageMasks, unsigned int); /** ******************** Fixed image regions ******************** */ /** Set the fixed image regions. */ virtual void SetFixedImageRegion(const FixedImageRegionType _arg, unsigned int pos); /** Set the first fixed image region. */ void SetFixedImageRegion(const FixedImageRegionType _arg) override { this->SetFixedImageRegion(_arg, 0); } /** Get the fixed image regions. */ virtual const FixedImageRegionType & GetFixedImageRegion(unsigned int pos) const; /** Get the first fixed image region. */ const FixedImageRegionType & GetFixedImageRegion() const override { return this->GetFixedImageRegion(0); } /** Set the number of fixed image regions. */ itkSetNumberOfMacro(FixedImageRegion); /** Get the number of fixed image regions. */ itkGetConstMacro(NumberOfFixedImageRegions, unsigned int); /** ******************** Moving images ******************** */ /** Set the moving images. */ virtual void SetMovingImage(const MovingImageType * _arg, unsigned int pos); /** Set the first moving image. */ void SetMovingImage(const MovingImageType * _arg) override { this->SetMovingImage(_arg, 0); } /** Get the moving images. */ virtual const MovingImageType * GetMovingImage(unsigned int pos) const; /** Get the first moving image. */ const MovingImageType * GetMovingImage() const override { return this->GetMovingImage(0); } /** Set the number of moving images. */ itkSetNumberOfMacro(MovingImage); /** Get the number of moving images. */ itkGetConstMacro(NumberOfMovingImages, unsigned int); /** ******************** Moving image masks ******************** */ /** Set the moving image masks. */ virtual void SetMovingImageMask(const MovingImageMaskType * _arg, unsigned int pos); /** Set the first moving image mask. */ void SetMovingImageMask(const MovingImageMaskType * _arg) override { this->SetMovingImageMask(_arg, 0); } /** Get the moving image masks. */ virtual const MovingImageMaskType * GetMovingImageMask(unsigned int pos) const; /** Get the first moving image mask. */ const MovingImageMaskType * GetMovingImageMask() const override { return this->GetMovingImageMask(0); } /** Set the number of moving image masks. */ itkSetNumberOfMacro(MovingImageMask); /** Get the number of moving image masks. */ itkGetConstMacro(NumberOfMovingImageMasks, unsigned int); /** ******************** Interpolators ******************** * These interpolators are used for the moving images. */ /** Set the interpolators. */ virtual void SetInterpolator(InterpolatorType * _arg, unsigned int pos); /** Set the first interpolator. */ void SetInterpolator(InterpolatorType * _arg) override { return this->SetInterpolator(_arg, 0); } /** Get the interpolators. */ virtual InterpolatorType * GetInterpolator(unsigned int pos) const; /** Get the first interpolator. */ InterpolatorType * GetInterpolator() override { return this->GetInterpolator(0); } /** Get the first interpolator. Const overload. */ const InterpolatorType * GetInterpolator() const override { return this->GetInterpolator(0); } /** Set the number of interpolators. */ itkSetNumberOfMacro(Interpolator); /** Get the number of interpolators. */ itkGetConstMacro(NumberOfInterpolators, unsigned int); /** A function to check if all moving image interpolators are of type B-spline. */ itkGetConstMacro(InterpolatorsAreBSpline, bool); /** ******************** FixedImageInterpolators ******************** * These interpolators are used for the fixed images. */ /** Set the fixed image interpolators. */ virtual void SetFixedImageInterpolator(FixedImageInterpolatorType * _arg, unsigned int pos); /** Set the first fixed image interpolator. */ virtual void SetFixedImageInterpolator(FixedImageInterpolatorType * _arg) { return this->SetFixedImageInterpolator(_arg, 0); } /** Get the fixed image interpolators. */ virtual FixedImageInterpolatorType * GetFixedImageInterpolator(unsigned int pos) const; /** Get the first fixed image interpolator. */ virtual FixedImageInterpolatorType * GetFixedImageInterpolator() const { return this->GetFixedImageInterpolator(0); } /** Set the number of fixed image interpolators. */ itkSetNumberOfMacro(FixedImageInterpolator); /** Get the number of fixed image interpolators. */ itkGetConstMacro(NumberOfFixedImageInterpolators, unsigned int); /** ******************** Other public functions ******************** */ /** Initialisation. */ void Initialize() override; protected: /** Constructor. */ MultiInputImageToImageMetricBase() = default; /** Destructor. */ ~MultiInputImageToImageMetricBase() override = default; /** Typedef's from the Superclass. */ using typename Superclass::MovingImagePointType; using typename Superclass::MovingImageIndexType; using typename Superclass::MovingImageDerivativeType; using typename Superclass::MovingImageContinuousIndexType; /** Typedef's for the moving image interpolators. */ using typename Superclass::BSplineInterpolatorType; using BSplineInterpolatorPointer = typename BSplineInterpolatorType::Pointer; using BSplineInterpolatorVectorType = std::vector; /** Initialize variables related to the image sampler; called by Initialize. */ void InitializeImageSampler() override; /** Check if all interpolators (for the moving image) are of type * BSplineInterpolateImageFunction. */ virtual void CheckForBSplineInterpolators(); /** Check if mappedPoint is inside all moving images. * If so, the moving image value and possibly derivative are computed. */ bool EvaluateMovingImageValueAndDerivative(const MovingImagePointType & mappedPoint, RealType & movingImageValue, MovingImageDerivativeType * gradient) const override; /** IsInsideMovingMask: Returns the AND of all moving image masks. */ bool IsInsideMovingMask(const MovingImagePointType & mappedPoint) const override; /** Protected member variables. */ FixedImageVectorType m_FixedImageVector{}; FixedImageMaskVectorType m_FixedImageMaskVector{}; FixedImageRegionVectorType m_FixedImageRegionVector{}; MovingImageVectorType m_MovingImageVector{}; MovingImageMaskVectorType m_MovingImageMaskVector{}; InterpolatorVectorType m_InterpolatorVector{}; FixedImageInterpolatorVectorType m_FixedImageInterpolatorVector{}; bool m_InterpolatorsAreBSpline{ false }; BSplineInterpolatorVectorType m_BSplineInterpolatorVector{}; private: // Private using-declarations, to avoid `-Woverloaded-virtual` warnings from GCC (GCC 11.4) or clang (macos-12). using Superclass::SetFixedImageMask; using Superclass::SetMovingImageMask; /// Avoids accidentally calling `this->FastEvaluateMovingImageValueAndDerivative(mappedPoint, ..., threadId)`, when /// `*this` is derived from `MultiInputImageToImageMetricBase`. (The non-virtual member function /// `AdvancedImageToImageMetric::FastEvaluateMovingImageValueAndDerivative` does not entirely replace the /// `MultiInputImageToImageMetricBase::EvaluateMovingImageValueAndDerivative` override.) void FastEvaluateMovingImageValueAndDerivative(...) const = delete; /** Private member variables. */ FixedImageRegionType m_DummyFixedImageRegion{}; unsigned int m_NumberOfFixedImages{ 0 }; unsigned int m_NumberOfFixedImageMasks{ 0 }; unsigned int m_NumberOfFixedImageRegions{ 0 }; unsigned int m_NumberOfMovingImages{ 0 }; unsigned int m_NumberOfMovingImageMasks{ 0 }; unsigned int m_NumberOfInterpolators{ 0 }; unsigned int m_NumberOfFixedImageInterpolators{ 0 }; }; } // end namespace itk #undef itkSetNumberOfMacro #ifndef ITK_MANUAL_INSTANTIATION # include "itkMultiInputImageToImageMetricBase.hxx" #endif #endif // end #ifndef itkMultiInputImageToImageMetricBase_h elastix-5.2.0/Common/CostFunctions/itkMultiInputImageToImageMetricBase.hxx000066400000000000000000000351751474534065100267670ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef _itkMultiInputImageToImageMetricBase_hxx #define _itkMultiInputImageToImageMetricBase_hxx #include "itkMultiInputImageToImageMetricBase.h" /** Macros to reduce some copy-paste work. * These macros provide the implementation of * all Set/GetFixedImage, Set/GetInterpolator etc methods * * The macros are undef'ed at the end of this file */ /** Macro for setting objects. */ #define itkImplementationSetObjectMacro(_name, _type) \ template \ void MultiInputImageToImageMetricBase::Set##_name(_type * _arg, unsigned int pos) \ { \ if (this->m_##_name##Vector.size() < pos + 1) \ { \ this->m_##_name##Vector.resize(pos + 1); \ this->m_NumberOf##_name##s = pos + 1; \ } \ if (pos == 0) \ { \ this->Superclass::Set##_name(_arg); \ } \ if (this->m_##_name##Vector[pos] != _arg) \ { \ this->m_##_name##Vector[pos] = _arg; \ this->Modified(); \ } \ } // comments for allowing ; after calling the macro /** Macro for setting objects. */ #define itkImplementationSetObjectMacro2(_name, _type) \ template \ void MultiInputImageToImageMetricBase::Set##_name(_type * _arg, unsigned int pos) \ { \ if (this->m_##_name##Vector.size() < pos + 1) \ { \ this->m_##_name##Vector.resize(pos + 1); \ this->m_NumberOf##_name##s = pos + 1; \ } \ if (this->m_##_name##Vector[pos] != _arg) \ { \ this->m_##_name##Vector[pos] = _arg; \ this->Modified(); \ } \ } // comments for allowing ; after calling the macro /** Macro for getting objects. */ #define itkImplementationGetObjectMacro(_name, _type) \ template \ auto MultiInputImageToImageMetricBase::Get##_name(unsigned int pos) const->_type * \ { \ if (this->m_##_name##Vector.size() < pos + 1) \ { \ return 0; \ } \ return this->m_##_name##Vector[pos]; \ } // comments for allowing ; after calling the macro /** Macro for getting const objects. */ #define itkImplementationGetConstObjectMacro(_name, _type) \ template \ auto MultiInputImageToImageMetricBase::Get##_name(unsigned int pos) const->const _type * \ { \ if (this->m_##_name##Vector.size() < pos + 1) \ { \ return 0; \ } \ return this->m_##_name##Vector[pos]; \ } // comments for allowing ; after calling the macro namespace itk { /** Set components. */ itkImplementationSetObjectMacro(FixedImage, const FixedImageType); itkImplementationSetObjectMacro(FixedImageMask, const FixedImageMaskType); itkImplementationSetObjectMacro(MovingImage, const MovingImageType); itkImplementationSetObjectMacro(MovingImageMask, const MovingImageMaskType); itkImplementationSetObjectMacro(Interpolator, InterpolatorType); itkImplementationSetObjectMacro2(FixedImageInterpolator, FixedImageInterpolatorType); /** Get components. */ itkImplementationGetConstObjectMacro(FixedImage, FixedImageType); itkImplementationGetConstObjectMacro(FixedImageMask, FixedImageMaskType); itkImplementationGetConstObjectMacro(MovingImage, MovingImageType); itkImplementationGetConstObjectMacro(MovingImageMask, MovingImageMaskType); itkImplementationGetObjectMacro(Interpolator, InterpolatorType); itkImplementationGetObjectMacro(FixedImageInterpolator, FixedImageInterpolatorType); /** * ************************ SetFixedImageRegion ************************* */ template void MultiInputImageToImageMetricBase::SetFixedImageRegion(const FixedImageRegionType _arg, unsigned int pos) { if (this->m_FixedImageRegionVector.size() < pos + 1) { this->m_FixedImageRegionVector.resize(pos + 1); this->m_NumberOfFixedImageRegions = pos + 1; } if (pos == 0) { this->Superclass::SetFixedImageRegion(_arg); } if (this->m_FixedImageRegionVector[pos] != _arg) { this->m_FixedImageRegionVector[pos] = _arg; this->Modified(); } } // end SetFixedImageRegion() /** * ************************ GetFixedImageRegion ************************* */ template auto MultiInputImageToImageMetricBase::GetFixedImageRegion(unsigned int pos) const -> const FixedImageRegionType & { if (this->m_FixedImageRegionVector.size() < pos) { return this->m_DummyFixedImageRegion; } return this->m_FixedImageRegionVector[pos]; } // end GetFixedImageRegion() /** * ****************** CheckForBSplineInterpolators ********************** */ template void MultiInputImageToImageMetricBase::CheckForBSplineInterpolators() { /** Check if the interpolators are of type BSplineInterpolateImageFunction. * If so, we can make use of its EvaluateDerivatives method. * Otherwise, an exception is thrown. */ this->m_InterpolatorsAreBSpline = true; this->m_BSplineInterpolatorVector.resize(this->m_NumberOfMovingImages); for (unsigned int i = 0; i < this->m_NumberOfMovingImages; ++i) { BSplineInterpolatorType * testPtr = dynamic_cast(this->m_InterpolatorVector[i].GetPointer()); if (testPtr) { this->m_BSplineInterpolatorVector[i] = testPtr; itkDebugMacro("Interpolator " << i << " is B-spline."); } else { this->m_InterpolatorsAreBSpline = false; itkDebugMacro("Interpolator " << i << " is NOT B-spline."); itkExceptionMacro("Interpolator " << i << " is NOT B-spline."); } } // end for-loop } // end CheckForBSplineInterpolators() /** * ****************** Initialize ********************** */ template void MultiInputImageToImageMetricBase::Initialize() { /** Connect the interpolators. */ for (unsigned int i = 0; i < this->GetNumberOfInterpolators(); ++i) { this->m_InterpolatorVector[i]->SetInputImage(this->m_MovingImageVector[i]); } /** Connect the fixed image interpolators. */ for (unsigned int i = 0; i < this->GetNumberOfFixedImageInterpolators(); ++i) { this->m_FixedImageInterpolatorVector[i]->SetInputImage(this->m_FixedImageVector[i]); } /** Check for B-spline interpolators. */ this->CheckForBSplineInterpolators(); /** Call the superclass' implementation. */ this->Superclass::Initialize(); } // end Initialize() /** * ********************* InitializeImageSampler **************************** */ template void MultiInputImageToImageMetricBase::InitializeImageSampler() { if (this->GetUseImageSampler()) { /** Check if the ImageSampler is set. */ if (!Superclass::m_ImageSampler) { itkExceptionMacro("ImageSampler is not present"); } /** Initialize the Image Sampler: set the fixed images. */ for (unsigned int i = 0; i < this->GetNumberOfFixedImages(); ++i) { Superclass::m_ImageSampler->SetInput(i, this->m_FixedImageVector[i]); } /** Initialize the Image Sampler: set the fixed image masks. */ for (unsigned int i = 0; i < this->GetNumberOfFixedImageMasks(); ++i) { Superclass::m_ImageSampler->SetMask(this->m_FixedImageMaskVector[i], i); } /** Initialize the Image Sampler: set the fixed image regions. */ for (unsigned int i = 0; i < this->GetNumberOfFixedImages(); ++i) { Superclass::m_ImageSampler->SetInputImageRegion(this->m_FixedImageRegionVector[i], i); } } } // end InitializeImageSampler() /** * ******************* EvaluateMovingImageValueAndDerivative ****************** */ template bool MultiInputImageToImageMetricBase::EvaluateMovingImageValueAndDerivative( const MovingImagePointType & mappedPoint, RealType & movingImageValue, MovingImageDerivativeType * gradient) const { /** Check if the mapped point is inside the moving image buffers of the feature images. */ bool sampleOk = true; for (unsigned int i = 1; i < this->GetNumberOfInterpolators(); ++i) { sampleOk &= this->GetInterpolator(i)->IsInsideBuffer(mappedPoint); /** If not inside this buffer we can quit. */ if (!sampleOk) { return false; } } /** Compute value and possibly derivative of the moving image. */ return this->Superclass::EvaluateMovingImageValueAndDerivative(mappedPoint, movingImageValue, gradient); } // end EvaluateMovingImageValueAndDerivative() /** * ************************ IsInsideMovingMask ************************* */ template bool MultiInputImageToImageMetricBase::IsInsideMovingMask( const MovingImagePointType & mappedPoint) const { /** If no moving image masks are present 'true' is returned, * meaning that this sample is taken into account. Otherwise, the * AND of all masks is returned, i.e. the sample should be inside * all masks. */ bool inside = true; for (unsigned int i = 0; i < this->GetNumberOfMovingImageMasks(); ++i) { if (const auto * const movingImageMask = this->GetMovingImageMask(i)) { inside &= movingImageMask->IsInsideInWorldSpace(mappedPoint); } /** If the point falls outside one mask, we can skip the rest. */ if (!inside) { return false; } } return inside; } // end IsInsideMovingMask() } // end namespace itk #undef itkImplementationSetObjectMacro #undef itkImplementationSetObjectMacro2 #undef itkImplementationGetObjectMacro #undef itkImplementationGetConstObjectMacro #endif // end #ifndef _itkMultiInputImageToImageMetricBase_hxx elastix-5.2.0/Common/CostFunctions/itkParzenWindowHistogramImageToImageMetric.h000066400000000000000000000546351474534065100300110ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkParzenWindowHistogramImageToImageMetric_h #define itkParzenWindowHistogramImageToImageMetric_h #include "itkAdvancedImageToImageMetric.h" #include "itkKernelFunctionBase2.h" #include namespace itk { /** * \class ParzenWindowHistogramImageToImageMetric * \brief A base class for image metrics based on a joint histogram * computed using Parzen Windowing * * The calculations are based on the method of Mattes/Thevenaz/Unser [1,2,3] * where the probability density distribution are estimated using * Parzen histograms. * * Once the PDF's have been constructed, the metric value and derivative * can be computed. Inheriting classes should make sure to call * the function ComputePDFs(AndPDFDerivatives) before using m_JointPDF and m_Alpha * (and m_JointPDFDerivatives). * * This class does not define the GetValue/GetValueAndDerivative methods. * This is the task of inheriting classes. * * The code is based on the itk::MattesMutualInformationImageToImageMetric, * but largely rewritten. Some important features: * - It inherits from AdvancedImageToImageMetric, which provides a lot of * general functionality. * - It splits up some functions in subfunctions. * - The Parzen window order can be chosen. * - A fixed and moving number of histogram bins can be chosen. * - More use of iterators instead of raw buffer pointers. * - An optional FiniteDifference derivative estimation. * * \warning This class is not thread safe due the member data structures * used to the store the sampled points and the marginal and joint pdfs. * * References:\n * [1] "Nonrigid multimodality image registration"\n * D. Mattes, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank\n * Medical Imaging 2001: Image Processing, 2001, pp. 1609-1620.\n * [2] "PET-CT Image Registration in the Chest Using Free-form Deformations"\n * D. Mattes, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank\n * IEEE Transactions in Medical Imaging. To Appear.\n * [3] "Optimization of Mutual Information for MultiResolution Image * Registration"\n * P. Thevenaz and M. Unser\n * IEEE Transactions in Image Processing, 9(12) December 2000.\n * * * \ingroup Metrics */ template class ITK_TEMPLATE_EXPORT ParzenWindowHistogramImageToImageMetric : public AdvancedImageToImageMetric { public: ITK_DISALLOW_COPY_AND_MOVE(ParzenWindowHistogramImageToImageMetric); /** Standard class typedefs. */ using Self = ParzenWindowHistogramImageToImageMetric; using Superclass = AdvancedImageToImageMetric; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Run-time type information (and related methods). */ itkTypeMacro(ParzenWindowHistogramImageToImageMetric, AdvancedImageToImageMetric); /** Typedefs from the superclass. */ using typename Superclass::CoordinateRepresentationType; using typename Superclass::MovingImageType; using typename Superclass::MovingImagePixelType; using typename Superclass::MovingImageConstPointer; using typename Superclass::FixedImageType; using typename Superclass::FixedImageConstPointer; using typename Superclass::FixedImageRegionType; using typename Superclass::TransformType; using typename Superclass::TransformPointer; using typename Superclass::InputPointType; using typename Superclass::OutputPointType; using typename Superclass::TransformParametersType; using typename Superclass::TransformJacobianType; using typename Superclass::InterpolatorType; using typename Superclass::InterpolatorPointer; using typename Superclass::RealType; using typename Superclass::GradientPixelType; using typename Superclass::GradientImageType; using typename Superclass::GradientImagePointer; using typename Superclass::FixedImageMaskType; using typename Superclass::FixedImageMaskPointer; using typename Superclass::MovingImageMaskType; using typename Superclass::MovingImageMaskPointer; using typename Superclass::MeasureType; using typename Superclass::DerivativeType; using typename Superclass::DerivativeValueType; using typename Superclass::ParametersType; using typename Superclass::FixedImagePixelType; using typename Superclass::MovingImageRegionType; using typename Superclass::ImageSamplerType; using typename Superclass::ImageSamplerPointer; using typename Superclass::ImageSampleContainerType; using typename Superclass::ImageSampleContainerPointer; using typename Superclass::FixedImageLimiterType; using typename Superclass::MovingImageLimiterType; using typename Superclass::FixedImageLimiterOutputType; using typename Superclass::MovingImageLimiterOutputType; using typename Superclass::MovingImageDerivativeScalesType; using typename Superclass::ThreadInfoType; /** The fixed image dimension. */ itkStaticConstMacro(FixedImageDimension, unsigned int, FixedImageType::ImageDimension); /** The moving image dimension. */ itkStaticConstMacro(MovingImageDimension, unsigned int, MovingImageType::ImageDimension); /** Initialize the Metric by * (1) Call the superclass' implementation * (2) InitializeHistograms() * (3) InitializeKernels() * (4) Resize AlphaDerivatives */ void Initialize() override; /** Get the derivatives of the match measure. This method simply calls the * the GetValueAndDerivative, since this will be mostly almost as fast * as just computing the derivative. */ void GetDerivative(const ParametersType & parameters, DerivativeType & Derivative) const override; /** Get the value and derivatives for single valued optimizers. * This method calls this->GetValueAndAnalyticDerivative or * this->GetValueAndFiniteDifferenceDerivative, depending on the bool * m_UseFiniteDifferenceDerivative. */ void GetValueAndDerivative(const ParametersType & parameters, MeasureType & value, DerivativeType & derivative) const override; /** Number of bins to use for the fixed image in the histogram. * Typical value is 32. The minimum value is 4 due to the padding * required by the Parzen windowing with a cubic B-spline kernel. Note * that even if the metric is used on binary images, the number of bins * should at least be equal to four. */ itkSetClampMacro(NumberOfFixedHistogramBins, unsigned long, 4, NumericTraits::max()); itkGetConstMacro(NumberOfFixedHistogramBins, unsigned long); /** Number of bins to use for the moving image in the histogram. * Typical value is 32. The minimum value is 4 due to the padding * required by the Parzen windowing with a cubic B-spline kernel. Note * that even if the metric is used on binary images, the number of bins * should at least be equal to four. */ itkSetClampMacro(NumberOfMovingHistogramBins, unsigned long, 4, NumericTraits::max()); itkGetConstMacro(NumberOfMovingHistogramBins, unsigned long); /** The B-spline order of the fixed Parzen window; default: 0 */ itkSetClampMacro(FixedKernelBSplineOrder, unsigned int, 0, 3); itkGetConstMacro(FixedKernelBSplineOrder, unsigned int); /** The B-spline order of the moving B-spline order; default: 3 */ itkSetClampMacro(MovingKernelBSplineOrder, unsigned int, 0, 3); itkGetConstMacro(MovingKernelBSplineOrder, unsigned int); /** Option to use explicit PDF derivatives, which requires a lot * of memory in case of many parameters. */ itkSetMacro(UseExplicitPDFDerivatives, bool); itkGetConstReferenceMacro(UseExplicitPDFDerivatives, bool); itkBooleanMacro(UseExplicitPDFDerivatives); /** Whether you plan to call the GetDerivative/GetValueAndDerivative method or not. * This option should be set before calling Initialize(); Default: false. */ itkSetMacro(UseDerivative, bool); itkGetConstMacro(UseDerivative, bool); /** Whether you want to use a finite difference implementation of the metric's derivative. * This option should be set before calling Initialize(); Default: false. */ itkSetMacro(UseFiniteDifferenceDerivative, bool); itkGetConstMacro(UseFiniteDifferenceDerivative, bool); /** For computing the finite difference derivative, the perturbation (delta) of the * transform parameters; default: 1.0. * mu_right= mu + delta*e_k */ itkSetMacro(FiniteDifferencePerturbation, double); itkGetConstMacro(FiniteDifferencePerturbation, double); protected: /** The constructor. */ ParzenWindowHistogramImageToImageMetric(); /** The destructor. */ ~ParzenWindowHistogramImageToImageMetric() override = default; /** Print Self. */ void PrintSelf(std::ostream & os, Indent indent) const override; /** Protected Typedefs ******************/ /** Typedefs inherited from superclass. */ using typename Superclass::FixedImageIndexType; using typename Superclass::FixedImageIndexValueType; using OffsetValueType = typename FixedImageType::OffsetValueType; using typename Superclass::MovingImageIndexType; using typename Superclass::FixedImagePointType; using typename Superclass::MovingImagePointType; using typename Superclass::MovingImageContinuousIndexType; using typename Superclass::BSplineInterpolatorType; using typename Superclass::MovingImageDerivativeType; using typename Superclass::NonZeroJacobianIndicesType; /** Typedefs for the PDFs and PDF derivatives. */ using PDFValueType = double; using PDFDerivativeValueType = float; using MarginalPDFType = Array; using JointPDFType = Image; using JointPDFPointer = typename JointPDFType::Pointer; using JointPDFDerivativesType = Image; using JointPDFDerivativesPointer = typename JointPDFDerivativesType::Pointer; using IncrementalMarginalPDFType = Image; using IncrementalMarginalPDFPointer = typename IncrementalMarginalPDFType::Pointer; using JointPDFIndexType = JointPDFType::IndexType; using JointPDFRegionType = JointPDFType::RegionType; using JointPDFSizeType = JointPDFType::SizeType; using JointPDFDerivativesIndexType = JointPDFDerivativesType::IndexType; using JointPDFDerivativesRegionType = JointPDFDerivativesType::RegionType; using JointPDFDerivativesSizeType = JointPDFDerivativesType::SizeType; using IncrementalMarginalPDFIndexType = IncrementalMarginalPDFType::IndexType; using IncrementalMarginalPDFRegionType = IncrementalMarginalPDFType::RegionType; using IncrementalMarginalPDFSizeType = IncrementalMarginalPDFType::SizeType; using ParzenValueContainerType = Array; /** Typedefs for Parzen kernel. */ using KernelFunctionType = KernelFunctionBase2; using KernelFunctionPointer = typename KernelFunctionType::Pointer; /** Protected variables **************************** */ /** Variables for Alpha (the normalization factor of the histogram). */ mutable double m_Alpha{ 0.0 }; mutable DerivativeType m_PerturbedAlphaRight{}; mutable DerivativeType m_PerturbedAlphaLeft{}; /** Variables for the pdfs (actually: histograms). */ mutable MarginalPDFType m_FixedImageMarginalPDF{}; mutable MarginalPDFType m_MovingImageMarginalPDF{}; JointPDFPointer m_JointPDF{ nullptr }; JointPDFDerivativesPointer m_JointPDFDerivatives{ nullptr }; JointPDFDerivativesPointer m_IncrementalJointPDFRight{}; JointPDFDerivativesPointer m_IncrementalJointPDFLeft{}; IncrementalMarginalPDFPointer m_FixedIncrementalMarginalPDFRight{ nullptr }; IncrementalMarginalPDFPointer m_MovingIncrementalMarginalPDFRight{ nullptr }; IncrementalMarginalPDFPointer m_FixedIncrementalMarginalPDFLeft{ nullptr }; IncrementalMarginalPDFPointer m_MovingIncrementalMarginalPDFLeft{ nullptr }; mutable JointPDFRegionType m_JointPDFWindow{}; // no need for mutable anymore? double m_MovingImageNormalizedMin{ 0.0 }; double m_FixedImageNormalizedMin{ 0.0 }; double m_FixedImageBinSize{ 0.0 }; double m_MovingImageBinSize{ 0.0 }; double m_FixedParzenTermToIndexOffset{ 0.5 }; double m_MovingParzenTermToIndexOffset{ -1.0 }; /** Kernels for computing Parzen histograms and derivatives. */ KernelFunctionPointer m_FixedKernel{ nullptr }; KernelFunctionPointer m_MovingKernel{ nullptr }; KernelFunctionPointer m_DerivativeMovingKernel{ nullptr }; /** Initialize threading related parameters. */ void InitializeThreadingParameters() const override; /** Multi-threaded versions of the ComputePDF function. */ void ThreadedComputePDFs(ThreadIdType threadId); /** Single-threadedly accumulate results. */ void AfterThreadedComputePDFs() const; /** Helper function to launch the threads. */ static ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION ComputePDFsThreaderCallback(void * arg); /** Helper function to launch the threads. */ void LaunchComputePDFsThreaderCallback() const; /** Compute the Parzen values given an image value and a starting histogram index * Compute the values at (parzenWindowIndex - parzenWindowTerm + k) for * k = 0 ... kernelsize-1 * Places the values in a buffer, which is supposed to have the right size already. */ static void EvaluateParzenValues(double parzenWindowTerm, OffsetValueType parzenWindowIndex, const KernelFunctionType & kernel, PDFValueType * parzenValues); /** Update the joint PDF with a pixel pair; on demand also updates the * pdf derivatives (if the Jacobian pointers are nonzero). */ virtual void UpdateJointPDFAndDerivatives(const RealType fixedImageValue, const RealType movingImageValue, const DerivativeType * imageJacobian, const NonZeroJacobianIndicesType * nzji, JointPDFType * jointPDF) const; /** Update the joint PDF and the incremental pdfs. * The input is a pixel pair (fixed, moving, moving mask) and * a set of moving image/mask values when using mu+delta*e_k, for * each k that has a nonzero Jacobian. And for mu-delta*e_k of course. * Also updates the PerturbedAlpha's * This function is used when UseFiniteDifferenceDerivative is true. * * \todo The IsInsideMovingMask return bools are converted to doubles (1 or 0) to * simplify the computation. But this may not be necessary. */ virtual void UpdateJointPDFAndIncrementalPDFs(RealType fixedImageValue, RealType movingImageValue, RealType movingMaskValue, const DerivativeType & movingImageValuesRight, const DerivativeType & movingImageValuesLeft, const DerivativeType & movingMaskValuesRight, const DerivativeType & movingMaskValuesLeft, const NonZeroJacobianIndicesType & nzji) const; /** Update the pdf derivatives * adds -image_jac[mu]*factor to the bin * with index [ mu, pdfIndex[0], pdfIndex[1] ] for all mu. * This function should only be called from UpdateJointPDFAndDerivatives. */ void UpdateJointPDFDerivatives(const JointPDFIndexType & pdfIndex, double factor, const DerivativeType & imageJacobian, const NonZeroJacobianIndicesType & nzji) const; /** Multiply the pdf entries by the given normalization factor. */ void NormalizeJointPDF(JointPDFType * pdf, const double factor) const; /** Multiply the pdf derivatives entries by the given normalization factor. */ void NormalizeJointPDFDerivatives(JointPDFDerivativesType * pdf, const double factor) const; /** Compute marginal pdfs by summing over the joint pdf * direction = 0: fixed marginal pdf * direction = 1: moving marginal pdf */ void ComputeMarginalPDF(const JointPDFType * jointPDF, MarginalPDFType & marginalPDF, const unsigned int direction) const; /** Compute incremental marginal pdfs. Integrates the incremental PDF * to obtain the fixed and moving marginal pdfs at once. */ virtual void ComputeIncrementalMarginalPDFs(const JointPDFDerivativesType * incrementalPDF, IncrementalMarginalPDFType * fixedIncrementalMarginalPDF, IncrementalMarginalPDFType * movingIncrementalMarginalPDF) const; /** Compute PDFs and pdf derivatives; Loops over the fixed image samples and constructs * the m_JointPDF, m_JointPDFDerivatives, and m_Alpha. * The JointPDF and Alpha and its derivatives are related as follows: * p = m_Alpha * m_JointPDF * dp/dmu = m_Alpha * m_JointPDFDerivatives * So, the JointPDF is more like a histogram than a true pdf... * The histograms are left unnormalized since it may be faster to * not do this explicitly. */ virtual void ComputePDFsAndPDFDerivatives(const ParametersType & parameters) const; /** Compute PDFs and incremental pdfs (which you can use to compute finite * difference estimate of the derivative). * Loops over the fixed image samples and constructs the m_JointPDF, * m_IncrementalJointPDF, m_Alpha, and m_PerturbedAlpha. * * mu = input parameters vector * jh(mu) = m_JointPDF(:,:) = joint histogram * ihr(k) = m_IncrementalJointPDFRight(k,:,:) * ihl(k) = m_IncrementalJointPDFLeft(k,:,:) * a(mu) = m_Alpha * par(k) = m_PerturbedAlphaRight(k) * pal(k) = m_PerturbedAlphaLeft(k) * size(ihr) = = size(ihl) = nrofparams * nrofmovingbins * nroffixedbins * * ihr and ihl are determined such that: * jh(mu+delta*e_k) = jh(mu) + ihr(k) * jh(mu-delta*e_k) = jh(mu) + ihl(k) * where e_k is the unit vector. * * the pdf can be derived with: * p(mu+delta*e_k) = ( par(k) ) * jh(mu+delta*e_k) * p(mu-delta*e_k) = ( pal(k) ) * jh(mu-delta*e_k) */ virtual void ComputePDFsAndIncrementalPDFs(const ParametersType & parameters) const; /** Compute PDFs; Loops over the fixed image samples and constructs * the m_JointPDF and m_Alpha * The JointPDF and Alpha are related as follows: * p = m_Alpha * m_JointPDF * So, the JointPDF is more like a histogram than a true pdf... * The histogram is left unnormalised since it may be faster to * not do this explicitly. */ virtual void ComputePDFsSingleThreaded(const ParametersType & parameters) const; virtual void ComputePDFs(const ParametersType & parameters) const; /** Some initialization functions, called by Initialize. */ virtual void InitializeHistograms(); virtual void InitializeKernels(); /** Get the value and analytic derivatives for single valued optimizers. * Called by GetValueAndDerivative if UseFiniteDifferenceDerivative == false * Implement this method in subclasses. */ virtual void GetValueAndAnalyticDerivative(const ParametersType & itkNotUsed(parameters), MeasureType & itkNotUsed(value), DerivativeType & itkNotUsed(derivative)) const {} /** Get the value and finite difference derivatives for single valued optimizers. * Called by GetValueAndDerivative if UseFiniteDifferenceDerivative == true * Implement this method in subclasses. */ virtual void GetValueAndFiniteDifferenceDerivative(const ParametersType & itkNotUsed(parameters), MeasureType & itkNotUsed(value), DerivativeType & itkNotUsed(derivative)) const {} private: /** Threading related parameters. */ mutable std::vector m_ThreaderJointPDFs{}; /** Helper structs that multi-threads the computation of * the metric derivative using ITK threads. */ struct ParzenWindowHistogramMultiThreaderParameterType // can't we use the one from AdvancedImageToImageMetric ? { Self * m_Metric; }; ParzenWindowHistogramMultiThreaderParameterType m_ParzenWindowHistogramThreaderParameters{}; struct ParzenWindowHistogramGetValueAndDerivativePerThreadStruct { SizeValueType st_NumberOfPixelsCounted; JointPDFPointer st_JointPDF; }; itkPadStruct(ITK_CACHE_LINE_ALIGNMENT, ParzenWindowHistogramGetValueAndDerivativePerThreadStruct, PaddedParzenWindowHistogramGetValueAndDerivativePerThreadStruct); itkAlignedTypedef(ITK_CACHE_LINE_ALIGNMENT, PaddedParzenWindowHistogramGetValueAndDerivativePerThreadStruct, AlignedParzenWindowHistogramGetValueAndDerivativePerThreadStruct); mutable std::vector m_ParzenWindowHistogramGetValueAndDerivativePerThreadVariables; /** Variables that can/should be accessed by their Set/Get functions. */ unsigned long m_NumberOfFixedHistogramBins{ 32 }; unsigned long m_NumberOfMovingHistogramBins{ 32 }; unsigned int m_FixedKernelBSplineOrder{ 0 }; unsigned int m_MovingKernelBSplineOrder{ 3 }; bool m_UseDerivative{ false }; bool m_UseExplicitPDFDerivatives{ true }; bool m_UseFiniteDifferenceDerivative{ false }; double m_FiniteDifferencePerturbation{ 1.0 }; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkParzenWindowHistogramImageToImageMetric.hxx" #endif #endif // end #ifndef itkParzenWindowHistogramImageToImageMetric_h elastix-5.2.0/Common/CostFunctions/itkParzenWindowHistogramImageToImageMetric.hxx000066400000000000000000001656371474534065100303760ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkParzenWindowHistogramImageToImageMetric_hxx #define itkParzenWindowHistogramImageToImageMetric_hxx #include "itkParzenWindowHistogramImageToImageMetric.h" #include "itkBSplineKernelFunction2.h" #include "itkBSplineDerivativeKernelFunction2.h" #include "itkImageLinearIteratorWithIndex.h" #include "itkImageScanlineIterator.h" #include #include namespace itk { /** * ********************* Constructor **************************** */ template ParzenWindowHistogramImageToImageMetric::ParzenWindowHistogramImageToImageMetric() { this->SetUseImageSampler(true); this->SetUseFixedImageLimiter(true); this->SetUseMovingImageLimiter(true); /** Initialize the m_ParzenWindowHistogramThreaderParameters */ this->m_ParzenWindowHistogramThreaderParameters.m_Metric = this; } // end Constructor /** * ********************* PrintSelf ****************************** */ template void ParzenWindowHistogramImageToImageMetric::PrintSelf(std::ostream & os, Indent indent) const { /** Call the superclass' PrintSelf. */ Superclass::PrintSelf(os, indent); /** Add debugging information. */ os << indent << "NumberOfFixedHistogramBins: " << this->m_NumberOfFixedHistogramBins << std::endl; os << indent << "NumberOfMovingHistogramBins: " << this->m_NumberOfMovingHistogramBins << std::endl; os << indent << "FixedKernelBSplineOrder: " << this->m_FixedKernelBSplineOrder << std::endl; os << indent << "MovingKernelBSplineOrder: " << this->m_MovingKernelBSplineOrder << std::endl; /*double m_MovingImageNormalizedMin; double m_FixedImageNormalizedMin; double m_FixedImageBinSize; double m_MovingImageBinSize; double m_FixedParzenTermToIndexOffset; double m_MovingParzenTermToIndexOffset; bool m_UseDerivative; m_UseExplicitPDFDerivatives bool m_UseFiniteDifferenceDerivative; double m_FiniteDifferencePerturbation;*/ /** This function is not complete, but we don't use it anyway. */ } // end PrintSelf() /** * ********************* Initialize ***************************** */ template void ParzenWindowHistogramImageToImageMetric::Initialize() { /** Call the superclass to check that standard components are available. */ this->Superclass::Initialize(); /** Set up the histograms. */ this->InitializeHistograms(); /** Set up the Parzen windows. */ this->InitializeKernels(); /** If the user plans to use a finite difference derivative, * allocate some memory for the perturbed alpha variables. */ if (this->GetUseDerivative() && this->GetUseFiniteDifferenceDerivative()) { this->m_PerturbedAlphaRight.SetSize(this->GetNumberOfParameters()); this->m_PerturbedAlphaLeft.SetSize(this->GetNumberOfParameters()); } else { this->m_PerturbedAlphaRight.SetSize(0); this->m_PerturbedAlphaLeft.SetSize(0); } } // end Initialize() /** * ****************** InitializeHistograms ***************************** */ template void ParzenWindowHistogramImageToImageMetric::InitializeHistograms() { /* Compute binsize for the histogram. * * The binsize for the image intensities needs to be adjusted so that * we can avoid dealing with boundary conditions using the cubic * spline as the Parzen window. We do this by increasing the size * of the bins so that the joint histogram becomes "padded" at the * borders. Because we are changing the binsize, * we also need to shift the minimum by the padded amount in order to * avoid minimum values filling in our padded region. * * Note that there can still be non-zero bin values in the padded region, * it's just that these bins will never be a central bin for the Parzen * window. */ // int fixedPadding = 2; // this will pad by 2 bins // int movingPadding = 2; // this will pad by 2 bins int fixedPadding = this->m_FixedKernelBSplineOrder / 2; // should be enough int movingPadding = this->m_MovingKernelBSplineOrder / 2; /** The ratio times the expected bin size will be added twice to the image range. */ const double smallNumberRatio = 0.001; const double smallNumberFixed = smallNumberRatio * (Superclass::m_FixedImageMaxLimit - Superclass::m_FixedImageMinLimit) / static_cast(this->m_NumberOfFixedHistogramBins - 2 * fixedPadding - 1); const double smallNumberMoving = smallNumberRatio * (Superclass::m_MovingImageMaxLimit - Superclass::m_MovingImageMinLimit) / static_cast(this->m_NumberOfFixedHistogramBins - 2 * movingPadding - 1); /** Compute binsizes. */ const double fixedHistogramWidth = static_cast( static_cast(this->m_NumberOfFixedHistogramBins) // requires cast to signed type! - 2.0 * fixedPadding - 1.0); this->m_FixedImageBinSize = (Superclass::m_FixedImageMaxLimit - Superclass::m_FixedImageMinLimit + 2.0 * smallNumberFixed) / fixedHistogramWidth; this->m_FixedImageBinSize = std::max(this->m_FixedImageBinSize, 1e-10); this->m_FixedImageBinSize = std::min(this->m_FixedImageBinSize, 1e+10); this->m_FixedImageNormalizedMin = (Superclass::m_FixedImageMinLimit - smallNumberFixed) / this->m_FixedImageBinSize - static_cast(fixedPadding); const double movingHistogramWidth = static_cast( static_cast(this->m_NumberOfMovingHistogramBins) // requires cast to signed type! - 2.0 * movingPadding - 1.0); this->m_MovingImageBinSize = (Superclass::m_MovingImageMaxLimit - Superclass::m_MovingImageMinLimit + 2.0 * smallNumberMoving) / movingHistogramWidth; this->m_MovingImageBinSize = std::max(this->m_MovingImageBinSize, 1e-10); this->m_MovingImageBinSize = std::min(this->m_MovingImageBinSize, 1e+10); this->m_MovingImageNormalizedMin = (Superclass::m_MovingImageMinLimit - smallNumberMoving) / this->m_MovingImageBinSize - static_cast(movingPadding); /** Allocate memory for the marginal PDF. */ this->m_FixedImageMarginalPDF.SetSize(this->m_NumberOfFixedHistogramBins); this->m_MovingImageMarginalPDF.SetSize(this->m_NumberOfMovingHistogramBins); /** Allocate memory for the joint PDF and joint PDF derivatives. */ /** First set these ones to zero */ this->m_FixedIncrementalMarginalPDFRight = nullptr; this->m_MovingIncrementalMarginalPDFRight = nullptr; this->m_FixedIncrementalMarginalPDFLeft = nullptr; this->m_MovingIncrementalMarginalPDFLeft = nullptr; /** For the joint PDF define a region starting from {0,0} * with size {this->m_NumberOfMovingHistogramBins, this->m_NumberOfFixedHistogramBins} * The dimension represents moving image Parzen window index * and fixed image Parzen window index, respectively. * The moving Parzen index is chosen as the first dimension, * because probably the moving B-spline kernel order will be larger * than the fixed B-spline kernel order and it is faster to iterate along * the first dimension. */ this->m_JointPDF = JointPDFType::New(); this->m_JointPDF->SetRegions(JointPDFSizeType{ m_NumberOfMovingHistogramBins, m_NumberOfFixedHistogramBins }); this->m_JointPDF->Allocate(); if (this->GetUseDerivative()) { /** For the derivatives of the joint PDF define a region starting from {0,0,0} * with size {GetNumberOfParameters(),m_NumberOfMovingHistogramBins, * m_NumberOfFixedHistogramBins}. The dimension represents transform parameters, * moving image Parzen window index and fixed image Parzen window index, * respectively. * For the incremental pdfs (used for finite difference derivative estimation) * the same size happens to be valid. */ const JointPDFDerivativesSizeType jointPDFDerivativesSize{ this->GetNumberOfParameters(), m_NumberOfMovingHistogramBins, m_NumberOfFixedHistogramBins }; if (this->GetUseFiniteDifferenceDerivative()) { this->m_JointPDFDerivatives = nullptr; this->m_IncrementalJointPDFRight = JointPDFDerivativesType::New(); this->m_IncrementalJointPDFLeft = JointPDFDerivativesType::New(); this->m_IncrementalJointPDFRight->SetRegions(jointPDFDerivativesSize); this->m_IncrementalJointPDFLeft->SetRegions(jointPDFDerivativesSize); this->m_IncrementalJointPDFRight->Allocate(); this->m_IncrementalJointPDFLeft->Allocate(); /** Also initialize the incremental marginal pdfs. */ const IncrementalMarginalPDFSizeType fixedIMPDFSize{ this->GetNumberOfParameters(), m_NumberOfFixedHistogramBins }; const IncrementalMarginalPDFSizeType movingIMPDFSize{ this->GetNumberOfParameters(), m_NumberOfMovingHistogramBins }; this->m_FixedIncrementalMarginalPDFRight = IncrementalMarginalPDFType::New(); this->m_MovingIncrementalMarginalPDFRight = IncrementalMarginalPDFType::New(); this->m_FixedIncrementalMarginalPDFLeft = IncrementalMarginalPDFType::New(); this->m_MovingIncrementalMarginalPDFLeft = IncrementalMarginalPDFType::New(); this->m_FixedIncrementalMarginalPDFRight->SetRegions(fixedIMPDFSize); this->m_MovingIncrementalMarginalPDFRight->SetRegions(movingIMPDFSize); this->m_FixedIncrementalMarginalPDFLeft->SetRegions(fixedIMPDFSize); this->m_MovingIncrementalMarginalPDFLeft->SetRegions(movingIMPDFSize); this->m_FixedIncrementalMarginalPDFRight->Allocate(); this->m_MovingIncrementalMarginalPDFRight->Allocate(); this->m_FixedIncrementalMarginalPDFLeft->Allocate(); this->m_MovingIncrementalMarginalPDFLeft->Allocate(); } // end if this->GetUseFiniteDifferenceDerivative() else { if (this->m_UseExplicitPDFDerivatives) { this->m_IncrementalJointPDFRight = nullptr; this->m_IncrementalJointPDFLeft = nullptr; this->m_JointPDFDerivatives = JointPDFDerivativesType::New(); this->m_JointPDFDerivatives->SetRegions(jointPDFDerivativesSize); this->m_JointPDFDerivatives->Allocate(); } else { /** De-allocate large amount of memory for the m_JointPDFDerivatives. */ // \todo Should not be allocated in the first place if (!this->m_JointPDFDerivatives.IsNull()) { this->m_JointPDFDerivatives->SetRegions(JointPDFDerivativesSizeType{}); this->m_JointPDFDerivatives->Allocate(); this->m_JointPDFDerivatives->GetPixelContainer()->Squeeze(); } } } } else { this->m_JointPDFDerivatives = nullptr; this->m_IncrementalJointPDFRight = nullptr; this->m_IncrementalJointPDFLeft = nullptr; } } // end InitializeHistograms() /** * ****************** InitializeKernels ***************************** */ template void ParzenWindowHistogramImageToImageMetric::InitializeKernels() { switch (this->m_FixedKernelBSplineOrder) { case 0: this->m_FixedKernel = BSplineKernelFunction2<0>::New(); break; case 1: this->m_FixedKernel = BSplineKernelFunction2<1>::New(); break; case 2: this->m_FixedKernel = BSplineKernelFunction2<2>::New(); break; case 3: this->m_FixedKernel = BSplineKernelFunction2<3>::New(); break; default: itkExceptionMacro( "The following FixedKernelBSplineOrder is not implemented: " << this->m_FixedKernelBSplineOrder); } // end switch FixedKernelBSplineOrder switch (this->m_MovingKernelBSplineOrder) { case 0: this->m_MovingKernel = BSplineKernelFunction2<0>::New(); /** The derivative of a zero order B-spline makes no sense. Using the * derivative of a first order gives a kind of finite difference idea * Anyway, if you plan to call GetValueAndDerivative you should use * a higher B-spline order. */ this->m_DerivativeMovingKernel = BSplineDerivativeKernelFunction2<1>::New(); break; case 1: this->m_MovingKernel = BSplineKernelFunction2<1>::New(); this->m_DerivativeMovingKernel = BSplineDerivativeKernelFunction2<1>::New(); break; case 2: this->m_MovingKernel = BSplineKernelFunction2<2>::New(); this->m_DerivativeMovingKernel = BSplineDerivativeKernelFunction2<2>::New(); break; case 3: this->m_MovingKernel = BSplineKernelFunction2<3>::New(); this->m_DerivativeMovingKernel = BSplineDerivativeKernelFunction2<3>::New(); break; default: itkExceptionMacro( "The following MovingKernelBSplineOrder is not implemented: " << this->m_MovingKernelBSplineOrder); } // end switch MovingKernelBSplineOrder /** The region of support of the Parzen window determines which bins * of the joint PDF are effected by the pair of image values. * For example, if we are using a cubic spline for the moving image Parzen * window, four bins are affected. If the fixed image Parzen window is * a zero-order spline (box car) only one bin is affected. */ /** Set the size of the Parzen window. */ JointPDFSizeType parzenWindowSize; parzenWindowSize[0] = this->m_MovingKernelBSplineOrder + 1; parzenWindowSize[1] = this->m_FixedKernelBSplineOrder + 1; this->m_JointPDFWindow.SetSize(parzenWindowSize); /** The ParzenIndex is the lowest bin number that is affected by a * pixel and computed as: * ParzenIndex = std::floor( ParzenTerm + ParzenTermToIndexOffset ) * where ParzenTermToIndexOffset = 1/2, 0, -1/2, or -1. */ this->m_FixedParzenTermToIndexOffset = 0.5 - static_cast(this->m_FixedKernelBSplineOrder) / 2.0; this->m_MovingParzenTermToIndexOffset = 0.5 - static_cast(this->m_MovingKernelBSplineOrder) / 2.0; } // end InitializeKernels() /** * ********************* InitializeThreadingParameters **************************** */ template void ParzenWindowHistogramImageToImageMetric::InitializeThreadingParameters() const { /** Call superclass implementation. */ Superclass::InitializeThreadingParameters(); /** Resize and initialize the threading related parameters. * The SetSize() functions do not resize the data when this is not * needed, which saves valuable re-allocation time. * Filling the potentially large vectors is performed later, in each thread, * which has performance benefits for larger vector sizes. */ /** Construct region size for the joint histograms. */ const JointPDFSizeType jointPDFSize{ m_NumberOfMovingHistogramBins, m_NumberOfFixedHistogramBins }; const ThreadIdType numberOfThreads = Self::GetNumberOfWorkUnits(); /** Only resize the array of structs when needed. */ m_ParzenWindowHistogramGetValueAndDerivativePerThreadVariables.resize(numberOfThreads); /** Some initialization. */ for (auto & perThreadVariable : m_ParzenWindowHistogramGetValueAndDerivativePerThreadVariables) { perThreadVariable.st_NumberOfPixelsCounted = SizeValueType{}; // Initialize the joint pdf JointPDFPointer & jointPDF = perThreadVariable.st_JointPDF; if (jointPDF.IsNull()) { jointPDF = JointPDFType::New(); } if (jointPDF->GetLargestPossibleRegion() != JointPDFRegionType(jointPDFSize)) { jointPDF->SetRegions(jointPDFSize); jointPDF->Allocate(); } } } // end InitializeThreadingParameters() /** * ******************** GetDerivative *************************** */ template void ParzenWindowHistogramImageToImageMetric::GetDerivative(const ParametersType & parameters, DerivativeType & derivative) const { /** Call the combined version, since the additional computation of * the value does not take extra time. */ MeasureType value; this->GetValueAndDerivative(parameters, value, derivative); } // end GetDerivative() /** * ******************** GetValueAndDerivative *************************** */ template void ParzenWindowHistogramImageToImageMetric::GetValueAndDerivative( const ParametersType & parameters, MeasureType & value, DerivativeType & derivative) const { if (this->GetUseFiniteDifferenceDerivative()) { this->GetValueAndFiniteDifferenceDerivative(parameters, value, derivative); } else { this->GetValueAndAnalyticDerivative(parameters, value, derivative); } } // end GetValueAndDerivative() /** * ********************** EvaluateParzenValues *************** */ template void ParzenWindowHistogramImageToImageMetric::EvaluateParzenValues( double parzenWindowTerm, OffsetValueType parzenWindowIndex, const KernelFunctionType & kernel, PDFValueType * const parzenValues) { kernel.Evaluate(static_cast(parzenWindowIndex) - parzenWindowTerm, parzenValues); } // end EvaluateParzenValues() /** * ********************** UpdateJointPDFAndDerivatives *************** */ template void ParzenWindowHistogramImageToImageMetric::UpdateJointPDFAndDerivatives( const RealType fixedImageValue, const RealType movingImageValue, const DerivativeType * imageJacobian, const NonZeroJacobianIndicesType * nzji, JointPDFType * jointPDF) const { using PDFIteratorType = ImageScanlineIterator; /** Determine Parzen window arguments (see eq. 6 of Mattes paper [2]). */ const double fixedImageParzenWindowTerm = fixedImageValue / this->m_FixedImageBinSize - this->m_FixedImageNormalizedMin; const double movingImageParzenWindowTerm = movingImageValue / this->m_MovingImageBinSize - this->m_MovingImageNormalizedMin; /** The lowest bin numbers affected by this pixel: */ const OffsetValueType fixedImageParzenWindowIndex = static_cast(std::floor(fixedImageParzenWindowTerm + this->m_FixedParzenTermToIndexOffset)); const OffsetValueType movingImageParzenWindowIndex = static_cast(std::floor(movingImageParzenWindowTerm + this->m_MovingParzenTermToIndexOffset)); /** The Parzen values. */ const auto numberOfFixedParzenValues = m_JointPDFWindow.GetSize()[1]; const auto numberOfMovingParzenValues = m_JointPDFWindow.GetSize()[0]; // Create a buffer of Parzen values for both the fixed and the moving image. const auto parzenValues = std::make_unique(numberOfFixedParzenValues + numberOfMovingParzenValues); PDFValueType * const fixedParzenValues = parzenValues.get(); PDFValueType * const movingParzenValues = parzenValues.get() + numberOfFixedParzenValues; Self::EvaluateParzenValues( fixedImageParzenWindowTerm, fixedImageParzenWindowIndex, *m_FixedKernel, fixedParzenValues); Self::EvaluateParzenValues( movingImageParzenWindowTerm, movingImageParzenWindowIndex, *m_MovingKernel, movingParzenValues); /** Position the JointPDFWindow. */ JointPDFIndexType pdfWindowIndex; pdfWindowIndex[0] = movingImageParzenWindowIndex; pdfWindowIndex[1] = fixedImageParzenWindowIndex; /** For thread-safety, make a local copy of the support region, * and use that one. Because each thread will modify it. */ JointPDFRegionType jointPDFWindow = this->m_JointPDFWindow; jointPDFWindow.SetIndex(pdfWindowIndex); PDFIteratorType it(jointPDF, jointPDFWindow); if (!imageJacobian) { /** Loop over the Parzen window region and increment the values. */ for (unsigned int f = 0; f < numberOfFixedParzenValues; ++f) { const double fv = fixedParzenValues[f]; for (unsigned int m = 0; m < numberOfMovingParzenValues; ++m) { it.Value() += static_cast(fv * movingParzenValues[m]); ++it; } it.NextLine(); } } else { /** Compute the derivatives of the moving Parzen window. */ ParzenValueContainerType derivativeMovingParzenValues(numberOfMovingParzenValues); Self::EvaluateParzenValues(movingImageParzenWindowTerm, movingImageParzenWindowIndex, *m_DerivativeMovingKernel, derivativeMovingParzenValues.data_block()); const double et = static_cast(this->m_MovingImageBinSize); /** Loop over the Parzen window region and increment the values * Also update the pdf derivatives. */ for (unsigned int f = 0; f < numberOfFixedParzenValues; ++f) { const double fv = fixedParzenValues[f]; const double fv_et = fv / et; for (unsigned int m = 0; m < numberOfMovingParzenValues; ++m) { it.Value() += static_cast(fv * movingParzenValues[m]); this->UpdateJointPDFDerivatives(it.GetIndex(), fv_et * derivativeMovingParzenValues[m], *imageJacobian, *nzji); ++it; } it.NextLine(); } } } // end UpdateJointPDFAndDerivatives() /** * *************** UpdateJointPDFDerivatives *************************** */ template void ParzenWindowHistogramImageToImageMetric::UpdateJointPDFDerivatives( const JointPDFIndexType & pdfIndex, double factor, const DerivativeType & imageJacobian, const NonZeroJacobianIndicesType & nzji) const { /** Get the pointer to the element with index [0, pdfIndex[0], pdfIndex[1]]. */ PDFDerivativeValueType * derivPtr = this->m_JointPDFDerivatives->GetBufferPointer() + (pdfIndex[0] * this->m_JointPDFDerivatives->GetOffsetTable()[1]) + (pdfIndex[1] * this->m_JointPDFDerivatives->GetOffsetTable()[2]); const auto numberOfParameters = this->GetNumberOfParameters(); if (nzji.size() == numberOfParameters) { /** Loop over all Jacobians. */ typename DerivativeType::const_iterator imjac = imageJacobian.begin(); for (unsigned int mu = 0; mu < numberOfParameters; ++mu) { *(derivPtr) -= static_cast((*imjac) * factor); ++derivPtr; ++imjac; } } else { /** Loop only over the non-zero Jacobians. */ for (unsigned int i = 0; i < imageJacobian.GetSize(); ++i) { const unsigned int mu = nzji[i]; PDFDerivativeValueType * ptr = derivPtr + mu; *(ptr) -= static_cast(imageJacobian[i] * factor); } } } // end UpdateJointPDFDerivatives() /** * *********************** NormalizeJointPDF *********************** */ template void ParzenWindowHistogramImageToImageMetric::NormalizeJointPDF(JointPDFType * pdf, const double factor) const { using JointPDFIteratorType = ImageScanlineIterator; JointPDFIteratorType it(pdf, pdf->GetBufferedRegion()); const PDFValueType castfac = static_cast(factor); while (!it.IsAtEnd()) { while (!it.IsAtEndOfLine()) { it.Value() *= castfac; ++it; } it.NextLine(); } } // end NormalizeJointPDF() /** * *********************** NormalizeJointPDFDerivatives *********************** */ template void ParzenWindowHistogramImageToImageMetric::NormalizeJointPDFDerivatives( JointPDFDerivativesType * pdf, const double factor) const { using JointPDFDerivativesIteratorType = ImageScanlineIterator; JointPDFDerivativesIteratorType it(pdf, pdf->GetBufferedRegion()); const PDFValueType castfac = static_cast(factor); while (!it.IsAtEnd()) { while (!it.IsAtEndOfLine()) { it.Value() *= castfac; ++it; } it.NextLine(); } } // end NormalizeJointPDFDerivatives() /** * ************************ ComputeMarginalPDF *********************** */ template void ParzenWindowHistogramImageToImageMetric::ComputeMarginalPDF( const JointPDFType * itkNotUsed(jointPDF), MarginalPDFType & marginalPDF, const unsigned int direction) const { using JointPDFLinearIterator = ImageLinearIteratorWithIndex; // \todo: bug? shouldn't this be over the function argument jointPDF ? JointPDFLinearIterator linearIter(this->m_JointPDF, this->m_JointPDF->GetBufferedRegion()); linearIter.SetDirection(direction); linearIter.GoToBegin(); unsigned int marginalIndex = 0; while (!linearIter.IsAtEnd()) { PDFValueType sum = 0.0; while (!linearIter.IsAtEndOfLine()) { sum += linearIter.Get(); ++linearIter; } marginalPDF[marginalIndex] = sum; linearIter.NextLine(); ++marginalIndex; } } // end ComputeMarginalPDFs() /** * ******************** ComputeIncrementalMarginalPDFs ******************* */ template void ParzenWindowHistogramImageToImageMetric::ComputeIncrementalMarginalPDFs( const JointPDFDerivativesType * incrementalPDF, IncrementalMarginalPDFType * fixedIncrementalMarginalPDF, IncrementalMarginalPDFType * movingIncrementalMarginalPDF) const { using IncIteratorType = itk::ImageRegionConstIterator; using IncMargIteratorType = itk::ImageLinearIteratorWithIndex; fixedIncrementalMarginalPDF->FillBuffer(PDFValueType{}); movingIncrementalMarginalPDF->FillBuffer(PDFValueType{}); IncIteratorType incit(incrementalPDF, incrementalPDF->GetLargestPossibleRegion()); IncMargIteratorType fixincit(fixedIncrementalMarginalPDF, fixedIncrementalMarginalPDF->GetLargestPossibleRegion()); IncMargIteratorType movincit(movingIncrementalMarginalPDF, movingIncrementalMarginalPDF->GetLargestPossibleRegion()); incit.GoToBegin(); fixincit.GoToBegin(); movincit.GoToBegin(); const auto numberOfParameters = this->GetNumberOfParameters(); /** Loop over the incremental pdf and update the incremental marginal pdfs. */ for (unsigned int f = 0; f < this->m_NumberOfFixedHistogramBins; ++f) { for (unsigned int m = 0; m < this->m_NumberOfMovingHistogramBins; ++m) { for (unsigned int p = 0; p < numberOfParameters; ++p) { fixincit.Value() += incit.Get(); movincit.Value() += incit.Get(); ++incit; ++fixincit; ++movincit; } fixincit.GoToBeginOfLine(); movincit.NextLine(); } fixincit.NextLine(); movincit.GoToBegin(); } } // end ComputeIncrementalMarginalPDFs() /** * ******************* UpdateJointPDFAndIncrementalPDFs ******************* */ template void ParzenWindowHistogramImageToImageMetric::UpdateJointPDFAndIncrementalPDFs( RealType fixedImageValue, RealType movingImageValue, RealType movingMaskValue, const DerivativeType & movingImageValuesRight, const DerivativeType & movingImageValuesLeft, const DerivativeType & movingMaskValuesRight, const DerivativeType & movingMaskValuesLeft, const NonZeroJacobianIndicesType & nzji) const { /** Pointers to the first pixels in the incremental joint pdfs. */ PDFDerivativeValueType * incRightBasePtr = this->m_IncrementalJointPDFRight->GetBufferPointer(); PDFDerivativeValueType * incLeftBasePtr = this->m_IncrementalJointPDFLeft->GetBufferPointer(); /** The Parzen value containers. */ ParzenValueContainerType fixedParzenValues(this->m_JointPDFWindow.GetSize()[1]); ParzenValueContainerType movingParzenValues(this->m_JointPDFWindow.GetSize()[0]); /** Determine fixed image Parzen window arguments (see eq. 6 of Mattes paper [2]). */ const double fixedImageParzenWindowTerm = fixedImageValue / this->m_FixedImageBinSize - this->m_FixedImageNormalizedMin; /** The lowest bin numbers affected by this pixel: */ const OffsetValueType fixedImageParzenWindowIndex = static_cast(std::floor(fixedImageParzenWindowTerm + this->m_FixedParzenTermToIndexOffset)); Self::EvaluateParzenValues( fixedImageParzenWindowTerm, fixedImageParzenWindowIndex, *m_FixedKernel, fixedParzenValues.data_block()); if (movingMaskValue > 1e-10) { /** Determine moving image Parzen window arguments (see eq. 6 of Mattes paper [2]). */ const double movingImageParzenWindowTerm = movingImageValue / this->m_MovingImageBinSize - this->m_MovingImageNormalizedMin; const OffsetValueType movingImageParzenWindowIndex = static_cast(std::floor(movingImageParzenWindowTerm + this->m_MovingParzenTermToIndexOffset)); Self::EvaluateParzenValues( movingImageParzenWindowTerm, movingImageParzenWindowIndex, *m_MovingKernel, movingParzenValues.data_block()); /** Position the JointPDFWindow (set the start index). */ JointPDFIndexType pdfIndex; pdfIndex[0] = movingImageParzenWindowIndex; pdfIndex[1] = fixedImageParzenWindowIndex; /** Loop over the Parzen window region and do the following update: * * m_JointPDF(M,F) += movingMask * fixedParzen(F) * movingParzen(M); * m_IncrementalJointPDF(k,M,F) -= movingMask * fixedParzen(F) * movingParzen(M); * for all k with nonzero Jacobian. */ for (unsigned int f = 0; f < fixedParzenValues.GetSize(); ++f) { const double fv_mask = fixedParzenValues[f] * movingMaskValue; for (unsigned int m = 0; m < movingParzenValues.GetSize(); ++m) { const PDFValueType fv_mask_mv = static_cast(fv_mask * movingParzenValues[m]); this->m_JointPDF->GetPixel(pdfIndex) += fv_mask_mv; unsigned long offset = static_cast(pdfIndex[0] * this->m_IncrementalJointPDFRight->GetOffsetTable()[1] + pdfIndex[1] * this->m_IncrementalJointPDFRight->GetOffsetTable()[2]); /** Get the pointer to the element with index [0, pdfIndex[0], pdfIndex[1]]. */ PDFDerivativeValueType * incRightPtr = incRightBasePtr + offset; PDFDerivativeValueType * incLeftPtr = incLeftBasePtr + offset; /** Loop only over the non-zero Jacobians. */ for (unsigned int i = 0; i < nzji.size(); ++i) { const unsigned int mu = nzji[i]; PDFDerivativeValueType * rPtr = incRightPtr + mu; PDFDerivativeValueType * lPtr = incLeftPtr + mu; *(rPtr) -= fv_mask_mv; *(lPtr) -= fv_mask_mv; } // end for i ++(pdfIndex[0]); } // end for m pdfIndex[0] = movingImageParzenWindowIndex; ++(pdfIndex[1]); } // end for f } // end if movingMaskValue > 1e-10 /** Loop only over the non-zero Jacobians and update the incremental pdfs and * update the perturbed alphas: * * m_IncrementalJointPDF(k,M,F) += * movingMask[k] * fixedParzen(F) * movingParzen(M)[k]; * m_PerturbedAlpha[k] += movingMask[k] - movingMask; * for all k with nonzero Jacobian. */ JointPDFDerivativesIndexType rindex; JointPDFDerivativesIndexType lindex; for (unsigned int i = 0; i < nzji.size(); ++i) { const unsigned int mu = nzji[i]; const double maskr = movingMaskValuesRight[i]; const double maskl = movingMaskValuesLeft[i]; if (maskr > 1e-10) { /** Compute Parzen stuff; note: we reuse the movingParzenValues container. */ const double movr = movingImageValuesRight[i]; const double movParzenWindowTermRight = movr / this->m_MovingImageBinSize - this->m_MovingImageNormalizedMin; const OffsetValueType movParzenWindowIndexRight = static_cast(std::floor(movParzenWindowTermRight + this->m_MovingParzenTermToIndexOffset)); Self::EvaluateParzenValues( movParzenWindowTermRight, movParzenWindowIndexRight, *m_MovingKernel, movingParzenValues.data_block()); /** Initialize index in IncrementalJointPDFRight. */ rindex[0] = mu; rindex[1] = movParzenWindowIndexRight; rindex[2] = fixedImageParzenWindowIndex; /** Loop over Parzen window and update IncrementalJointPDFRight. */ for (unsigned int f = 0; f < fixedParzenValues.GetSize(); ++f) { const double fv_mask = fixedParzenValues[f] * maskr; for (unsigned int m = 0; m < movingParzenValues.GetSize(); ++m) { const PDFValueType fv_mask_mv = static_cast(fv_mask * movingParzenValues[m]); this->m_IncrementalJointPDFRight->GetPixel(rindex) += fv_mask_mv; ++(rindex[1]); } // end for m ++(rindex[2]); rindex[1] = movParzenWindowIndexRight; } // end for f } // end if maskr if (maskl > 1e-10) { /** Compute Parzen stuff; note: we reuse the movingParzenValues container. */ const double movl = movingImageValuesLeft[i]; const double movParzenWindowTermLeft = movl / this->m_MovingImageBinSize - this->m_MovingImageNormalizedMin; const OffsetValueType movParzenWindowIndexLeft = static_cast(std::floor(movParzenWindowTermLeft + this->m_MovingParzenTermToIndexOffset)); Self::EvaluateParzenValues( movParzenWindowTermLeft, movParzenWindowIndexLeft, *m_MovingKernel, movingParzenValues.data_block()); /** Initialize index in IncrementalJointPDFLeft. */ lindex[0] = mu; lindex[1] = movParzenWindowIndexLeft; lindex[2] = fixedImageParzenWindowIndex; /** Loop over Parzen window and update IncrementalJointPDFLeft. */ for (unsigned int f = 0; f < fixedParzenValues.GetSize(); ++f) { const double fv_mask = fixedParzenValues[f] * maskl; for (unsigned int m = 0; m < movingParzenValues.GetSize(); ++m) { const PDFValueType fv_mask_mv = static_cast(fv_mask * movingParzenValues[m]); this->m_IncrementalJointPDFLeft->GetPixel(lindex) += fv_mask_mv; ++(lindex[1]); } // end for m ++(lindex[2]); lindex[1] = movParzenWindowIndexLeft; } // end for f } // end if maskl /** Update the perturbed alphas. */ this->m_PerturbedAlphaRight[mu] += (maskr - movingMaskValue); this->m_PerturbedAlphaLeft[mu] += (maskl - movingMaskValue); } // end for i } // end UpdateJointPDFAndIncrementalPDFs() /** * ************************ ComputePDFsSingleThreaded ************************** */ template void ParzenWindowHistogramImageToImageMetric::ComputePDFsSingleThreaded( const ParametersType & parameters) const { /** Initialize some variables. */ this->m_JointPDF->FillBuffer(0.0); Superclass::m_NumberOfPixelsCounted = 0; this->m_Alpha = 0.0; /** Call non-thread-safe stuff, such as: * this->SetTransformParameters( parameters ); * this->GetImageSampler()->Update(); * Because of these calls GetValueAndDerivative itself is not thread-safe, * so cannot be called multiple times simultaneously. * This is however needed in the CombinationImageToImageMetric. * In that case, you need to: * - switch the use of this function to on, using m_UseMetricSingleThreaded = true * - call BeforeThreadedGetValueAndDerivative once (single-threaded) before * calling GetValueAndDerivative * - switch the use of this function to off, using m_UseMetricSingleThreaded = false * - Now you can call GetValueAndDerivative multi-threaded. */ this->BeforeThreadedGetValueAndDerivative(parameters); /** Get a handle to the sample container. */ ImageSampleContainerPointer sampleContainer = this->GetImageSampler()->GetOutput(); /** Loop over sample container and compute contribution of each sample to pdfs. */ for (const auto & fixedImageSample : *sampleContainer) { /** Read fixed coordinates and initialize some variables. */ const FixedImagePointType & fixedPoint = fixedImageSample.m_ImageCoordinates; RealType movingImageValue; /** Transform point. */ const MovingImagePointType mappedPoint = this->TransformPoint(fixedPoint); /** Check if the point is inside the moving mask. */ bool sampleOk = this->IsInsideMovingMask(mappedPoint); /** Compute the moving image value and check if the point is * inside the moving image buffer. */ if (sampleOk) { sampleOk = this->Superclass::EvaluateMovingImageValueAndDerivative(mappedPoint, movingImageValue, nullptr); } if (sampleOk) { Superclass::m_NumberOfPixelsCounted++; /** Get the fixed image value. */ auto fixedImageValue = static_cast(fixedImageSample.m_ImageValue); /** Make sure the values fall within the histogram range. */ fixedImageValue = this->GetFixedImageLimiter()->Evaluate(fixedImageValue); movingImageValue = this->GetMovingImageLimiter()->Evaluate(movingImageValue); /** Compute this sample's contribution to the joint distributions. */ this->UpdateJointPDFAndDerivatives( fixedImageValue, movingImageValue, nullptr, nullptr, this->m_JointPDF.GetPointer()); } } // end iterating over fixed image spatial sample container for loop /** Check if enough samples were valid. */ this->CheckNumberOfSamples(sampleContainer->Size(), Superclass::m_NumberOfPixelsCounted); /** Compute alpha. */ this->m_Alpha = 1.0 / static_cast(Superclass::m_NumberOfPixelsCounted); } // end ComputePDFsSingleThreaded() /** * ************************ ComputePDFs ************************** */ template void ParzenWindowHistogramImageToImageMetric::ComputePDFs(const ParametersType & parameters) const { /** Option for now to still use the single threaded code. */ if (!Superclass::m_UseMultiThread) { return this->ComputePDFsSingleThreaded(parameters); } /** Call non-thread-safe stuff, such as: * this->SetTransformParameters( parameters ); * this->GetImageSampler()->Update(); * Because of these calls GetValueAndDerivative itself is not thread-safe, * so cannot be called multiple times simultaneously. * This is however needed in the CombinationImageToImageMetric. * In that case, you need to: * - switch the use of this function to on, using m_UseMetricSingleThreaded = true * - call BeforeThreadedGetValueAndDerivative once (single-threaded) before * calling GetValueAndDerivative * - switch the use of this function to off, using m_UseMetricSingleThreaded = false * - Now you can call GetValueAndDerivative multi-threaded. */ this->BeforeThreadedGetValueAndDerivative(parameters); /** Launch multi-threading JointPDF computation. */ this->LaunchComputePDFsThreaderCallback(); /** Gather the results from all threads. */ this->AfterThreadedComputePDFs(); } // end ComputePDFs() /** * ******************* ThreadedComputePDFs ******************* */ template void ParzenWindowHistogramImageToImageMetric::ThreadedComputePDFs(ThreadIdType threadId) { /** Get a handle to the pre-allocated joint PDF for the current thread. * The initialization is performed here, so that it is done multi-threadedly * instead of sequentially in InitializeThreadingParameters(). */ JointPDFPointer & jointPDF = this->m_ParzenWindowHistogramGetValueAndDerivativePerThreadVariables[threadId].st_JointPDF; jointPDF->FillBuffer(PDFValueType{}); /** Get a handle to the sample container. */ ImageSampleContainerPointer sampleContainer = this->GetImageSampler()->GetOutput(); const unsigned long sampleContainerSize = sampleContainer->Size(); /** Get the samples for this thread. */ const unsigned long nrOfSamplesPerThreads = static_cast( std::ceil(static_cast(sampleContainerSize) / static_cast(Self::GetNumberOfWorkUnits()))); const auto pos_begin = std::min(nrOfSamplesPerThreads * threadId, sampleContainerSize); const auto pos_end = std::min(nrOfSamplesPerThreads * (threadId + 1), sampleContainerSize); /** Create iterator over the sample container. */ const auto beginOfSampleContainer = sampleContainer->cbegin(); const auto fbegin = beginOfSampleContainer + pos_begin; const auto fend = beginOfSampleContainer + pos_end; /** Create variables to store intermediate results. circumvent false sharing */ unsigned long numberOfPixelsCounted = 0; /** Loop over sample container and compute contribution of each sample to pdfs. */ for (auto fiter = fbegin; fiter != fend; ++fiter) { /** Read fixed coordinates and initialize some variables. */ const FixedImagePointType & fixedPoint = fiter->m_ImageCoordinates; RealType movingImageValue; /** Transform point. */ const MovingImagePointType mappedPoint = this->TransformPoint(fixedPoint); /** Check if the point is inside the moving mask. */ bool sampleOk = this->IsInsideMovingMask(mappedPoint); /** Compute the moving image value and check if the point is * inside the moving image buffer. */ if (sampleOk) { sampleOk = this->FastEvaluateMovingImageValueAndDerivative(mappedPoint, movingImageValue, nullptr, threadId); } if (sampleOk) { ++numberOfPixelsCounted; /** Get the fixed image value. */ RealType fixedImageValue = static_cast(fiter->m_ImageValue); /** Make sure the values fall within the histogram range. */ fixedImageValue = this->GetFixedImageLimiter()->Evaluate(fixedImageValue); movingImageValue = this->GetMovingImageLimiter()->Evaluate(movingImageValue); /** Compute this sample's contribution to the joint distributions. */ this->UpdateJointPDFAndDerivatives(fixedImageValue, movingImageValue, nullptr, nullptr, jointPDF.GetPointer()); } } // end iterating over fixed image spatial sample container for loop /** Only update these variables at the end to prevent unnecessary "false sharing". */ this->m_ParzenWindowHistogramGetValueAndDerivativePerThreadVariables[threadId].st_NumberOfPixelsCounted = numberOfPixelsCounted; } // end ThreadedComputePDFs() /** * ******************* AfterThreadedComputePDFs ******************* */ template void ParzenWindowHistogramImageToImageMetric::AfterThreadedComputePDFs() const { const ThreadIdType numberOfThreads = Self::GetNumberOfWorkUnits(); /** Accumulate the number of pixels. */ Superclass::m_NumberOfPixelsCounted = this->m_ParzenWindowHistogramGetValueAndDerivativePerThreadVariables[0].st_NumberOfPixelsCounted; for (ThreadIdType i = 1; i < numberOfThreads; ++i) { Superclass::m_NumberOfPixelsCounted += this->m_ParzenWindowHistogramGetValueAndDerivativePerThreadVariables[i].st_NumberOfPixelsCounted; } /** Check if enough samples were valid. */ ImageSampleContainerPointer sampleContainer = this->GetImageSampler()->GetOutput(); this->CheckNumberOfSamples(sampleContainer->Size(), Superclass::m_NumberOfPixelsCounted); /** Compute alpha. */ this->m_Alpha = 1.0 / static_cast(Superclass::m_NumberOfPixelsCounted); /** Accumulate joint histogram. */ // could be multi-threaded too, by each thread updating only a part of the JointPDF. using JointPDFIteratorType = ImageScanlineIterator; JointPDFIteratorType it(this->m_JointPDF, this->m_JointPDF->GetBufferedRegion()); std::vector itT(numberOfThreads); for (ThreadIdType i = 0; i < numberOfThreads; ++i) { itT[i] = JointPDFIteratorType(this->m_ParzenWindowHistogramGetValueAndDerivativePerThreadVariables[i].st_JointPDF, this->m_JointPDF->GetBufferedRegion()); } PDFValueType sum; while (!it.IsAtEnd()) { while (!it.IsAtEndOfLine()) { sum = PDFValueType{}; for (ThreadIdType i = 0; i < numberOfThreads; ++i) { sum += itT[i].Value(); ++itT[i]; } it.Set(sum); ++it; } it.NextLine(); for (ThreadIdType i = 0; i < numberOfThreads; ++i) { itT[i].NextLine(); } } } // end AfterThreadedComputePDFs() /** * **************** ComputePDFsThreaderCallback ******* */ template ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION ParzenWindowHistogramImageToImageMetric::ComputePDFsThreaderCallback(void * arg) { assert(arg); const auto & infoStruct = *static_cast(arg); ThreadIdType threadId = infoStruct.WorkUnitID; assert(infoStruct.UserData); const auto & userData = *static_cast(infoStruct.UserData); userData.m_Metric->ThreadedComputePDFs(threadId); return ITK_THREAD_RETURN_DEFAULT_VALUE; } // end ComputePDFsThreaderCallback() /** * *********************** LaunchComputePDFsThreaderCallback*************** */ template void ParzenWindowHistogramImageToImageMetric::LaunchComputePDFsThreaderCallback() const { /** Setup threader and launch. */ this->m_Threader->SetSingleMethodAndExecute( this->ComputePDFsThreaderCallback, const_cast(static_cast(&this->m_ParzenWindowHistogramThreaderParameters))); } // end LaunchComputePDFsThreaderCallback() /** * ************************ ComputePDFsAndPDFDerivatives ******************* */ template void ParzenWindowHistogramImageToImageMetric::ComputePDFsAndPDFDerivatives( const ParametersType & parameters) const { /** Initialize some variables. */ this->m_JointPDF->FillBuffer(0.0); this->m_JointPDFDerivatives->FillBuffer(0.0); this->m_Alpha = 0.0; Superclass::m_NumberOfPixelsCounted = 0; /** Array that stores dM(x)/dmu, and the sparse jacobian+indices. */ NonZeroJacobianIndicesType nzji(Superclass::m_AdvancedTransform->GetNumberOfNonZeroJacobianIndices()); DerivativeType imageJacobian(nzji.size()); TransformJacobianType jacobian; /** Call non-thread-safe stuff, such as: * this->SetTransformParameters( parameters ); * this->GetImageSampler()->Update(); * Because of these calls GetValueAndDerivative itself is not thread-safe, * so cannot be called multiple times simultaneously. * This is however needed in the CombinationImageToImageMetric. * In that case, you need to: * - switch the use of this function to on, using m_UseMetricSingleThreaded = true * - call BeforeThreadedGetValueAndDerivative once (single-threaded) before * calling GetValueAndDerivative * - switch the use of this function to off, using m_UseMetricSingleThreaded = false * - Now you can call GetValueAndDerivative multi-threaded. */ this->BeforeThreadedGetValueAndDerivative(parameters); /** Get a handle to the sample container. */ ImageSampleContainerPointer sampleContainer = this->GetImageSampler()->GetOutput(); /** Loop over sample container and compute contribution of each sample to pdfs. */ for (const auto & fixedImageSample : *sampleContainer) { /** Read fixed coordinates and initialize some variables. */ const FixedImagePointType & fixedPoint = fixedImageSample.m_ImageCoordinates; RealType movingImageValue; MovingImageDerivativeType movingImageDerivative; /** Transform point. */ const MovingImagePointType mappedPoint = this->TransformPoint(fixedPoint); /** Check if the point is inside the moving mask. */ bool sampleOk = this->IsInsideMovingMask(mappedPoint); /** Compute the moving image value M(T(x)) and derivative dM/dx and check if * the point is inside the moving image buffer. */ if (sampleOk) { sampleOk = this->Superclass::EvaluateMovingImageValueAndDerivative(mappedPoint, movingImageValue, &movingImageDerivative); } if (sampleOk) { Superclass::m_NumberOfPixelsCounted++; /** Get the fixed image value. */ auto fixedImageValue = static_cast(fixedImageSample.m_ImageValue); /** Make sure the values fall within the histogram range. */ fixedImageValue = this->GetFixedImageLimiter()->Evaluate(fixedImageValue); movingImageValue = this->GetMovingImageLimiter()->Evaluate(movingImageValue, movingImageDerivative); /** Get the TransformJacobian dT/dmu. */ this->EvaluateTransformJacobian(fixedPoint, jacobian, nzji); /** Compute the inner product (dM/dx)^T (dT/dmu). */ this->EvaluateTransformJacobianInnerProduct(jacobian, movingImageDerivative, imageJacobian); /** Update the joint pdf and the joint pdf derivatives. */ this->UpdateJointPDFAndDerivatives( fixedImageValue, movingImageValue, &imageJacobian, &nzji, this->m_JointPDF.GetPointer()); } // end if-block check sampleOk } // end iterating over fixed image spatial sample container for loop /** Check if enough samples were valid. */ this->CheckNumberOfSamples(sampleContainer->Size(), Superclass::m_NumberOfPixelsCounted); /** Compute alpha. */ this->m_Alpha = 0.0; if (Superclass::m_NumberOfPixelsCounted > 0) { this->m_Alpha = 1.0 / static_cast(Superclass::m_NumberOfPixelsCounted); } } // end ComputePDFsAndPDFDerivatives() /** * ************************ ComputePDFsAndIncrementalPDFs ******************* */ template void ParzenWindowHistogramImageToImageMetric::ComputePDFsAndIncrementalPDFs( const ParametersType & parameters) const { /** Initialize some variables. */ this->m_JointPDF->FillBuffer(0.0); this->m_IncrementalJointPDFRight->FillBuffer(0.0); this->m_IncrementalJointPDFLeft->FillBuffer(0.0); this->m_Alpha = 0.0; this->m_PerturbedAlphaRight.Fill(0.0); this->m_PerturbedAlphaLeft.Fill(0.0); Superclass::m_NumberOfPixelsCounted = 0; double sumOfMovingMaskValues = 0.0; const double delta = this->GetFiniteDifferencePerturbation(); /** sparse jacobian+indices. */ NonZeroJacobianIndicesType nzji(Superclass::m_AdvancedTransform->GetNumberOfNonZeroJacobianIndices()); TransformJacobianType jacobian; /** Arrays that store dM(x)/dmu and dMask(x)/dmu. */ DerivativeType movingImageValuesRight(nzji.size()); DerivativeType movingImageValuesLeft(nzji.size()); DerivativeType movingMaskValuesRight(nzji.size()); DerivativeType movingMaskValuesLeft(nzji.size()); /** Call non-thread-safe stuff, such as: * this->SetTransformParameters( parameters ); * this->GetImageSampler()->Update(); * Because of these calls GetValueAndDerivative itself is not thread-safe, * so cannot be called multiple times simultaneously. * This is however needed in the CombinationImageToImageMetric. * In that case, you need to: * - switch the use of this function to on, using m_UseMetricSingleThreaded = true * - call BeforeThreadedGetValueAndDerivative once (single-threaded) before * calling GetValueAndDerivative * - switch the use of this function to off, using m_UseMetricSingleThreaded = false * - Now you can call GetValueAndDerivative multi-threaded. */ this->BeforeThreadedGetValueAndDerivative(parameters); /** Get a handle to the sample container. */ ImageSampleContainerPointer sampleContainer = this->GetImageSampler()->GetOutput(); /** Loop over sample container and compute contribution of each sample to pdfs. */ for (const auto & fixedImageSample : *sampleContainer) { /** Read fixed coordinates. */ const FixedImagePointType & fixedPoint = fixedImageSample.m_ImageCoordinates; /** Transform point and check if it is inside the B-spline support region. * if not, skip this sample. */ const MovingImagePointType mappedPoint = this->TransformPoint(fixedPoint); { /** Get the fixed image value and make sure the value falls within the histogram range. */ auto fixedImageValue = static_cast(fixedImageSample.m_ImageValue); fixedImageValue = this->GetFixedImageLimiter()->Evaluate(fixedImageValue); /** Check if the point is inside the moving mask. */ bool sampleOk = this->IsInsideMovingMask(mappedPoint); RealType movingMaskValue = static_cast(static_cast(sampleOk)); /** Compute the moving image value M(T(x)) and check if * the point is inside the moving image buffer. */ RealType movingImageValue{}; if (sampleOk) { sampleOk = this->Superclass::EvaluateMovingImageValueAndDerivative(mappedPoint, movingImageValue, nullptr); if (sampleOk) { movingImageValue = this->GetMovingImageLimiter()->Evaluate(movingImageValue); } else { /** this movingImageValueRight is invalid, even though the mask indicated it is valid. */ movingMaskValue = 0.0; } } /** Stop with this sample. It may be possible that with a perturbed parameter * a valid voxel pair is obtained, but: * - this chance is small, * - quitting now saves a lot of time, especially because this situation * occurs at border pixels (there are a lot of those) * - if we would analytically compute the gradient the same choice is * somehow made. */ if (!sampleOk) { continue; } /** Count how many samples were used. */ sumOfMovingMaskValues += movingMaskValue; Superclass::m_NumberOfPixelsCounted += static_cast(sampleOk); /** Get the TransformJacobian dT/dmu. We assume the transform is a linear * function of its parameters, so that we can evaluate T(x;\mu+delta_ek) * as T(x) + delta * dT/dmu_k. */ this->EvaluateTransformJacobian(fixedPoint, jacobian, nzji); MovingImagePointType mappedPointRight; MovingImagePointType mappedPointLeft; /** Loop over all parameters to perturb (parameters with nonzero Jacobian). */ for (unsigned int i = 0; i < nzji.size(); ++i) { /** Compute the transformed input point after perturbation. */ for (unsigned int j = 0; j < MovingImageDimension; ++j) { const double delta_jac = delta * jacobian[j][i]; mappedPointRight[j] = mappedPoint[j] + delta_jac; mappedPointLeft[j] = mappedPoint[j] - delta_jac; } /** Compute the moving mask 'value' and moving image value at the right perturbed positions. */ sampleOk = this->IsInsideMovingMask(mappedPointRight); RealType movingMaskValueRight = static_cast(static_cast(sampleOk)); if (sampleOk) { RealType movingImageValueRight = 0.0; sampleOk = this->Superclass::EvaluateMovingImageValueAndDerivative(mappedPointRight, movingImageValueRight, nullptr); if (sampleOk) { movingImageValueRight = this->GetMovingImageLimiter()->Evaluate(movingImageValueRight); movingImageValuesRight[i] = movingImageValueRight; } else { /** this movingImageValueRight is invalid, even though the mask indicated it is valid. */ movingMaskValueRight = 0.0; } } movingMaskValuesRight[i] = movingMaskValueRight; /** Compute the moving mask and moving image value at the left perturbed positions. */ sampleOk = this->IsInsideMovingMask(mappedPointLeft); RealType movingMaskValueLeft = static_cast(static_cast(sampleOk)); if (sampleOk) { RealType movingImageValueLeft = 0.0; sampleOk = this->Superclass::EvaluateMovingImageValueAndDerivative(mappedPointLeft, movingImageValueLeft, nullptr); if (sampleOk) { movingImageValueLeft = this->GetMovingImageLimiter()->Evaluate(movingImageValueLeft); movingImageValuesLeft[i] = movingImageValueLeft; } else { /** this movingImageValueLeft is invalid, even though the mask indicated it is valid. */ movingMaskValueLeft = 0.0; } } movingMaskValuesLeft[i] = movingMaskValueLeft; } // next parameter to perturb /** Update the joint pdf and the incremental joint pdfs, and the * perturbed alpha arrays. */ this->UpdateJointPDFAndIncrementalPDFs(fixedImageValue, movingImageValue, movingMaskValue, movingImageValuesRight, movingImageValuesLeft, movingMaskValuesRight, movingMaskValuesLeft, nzji); } // end if-block check sampleOk } // end iterating over fixed image spatial sample container for loop /** Check if enough samples were valid. */ this->CheckNumberOfSamples(sampleContainer->Size(), Superclass::m_NumberOfPixelsCounted); /** Compute alpha and its perturbed versions. */ this->m_Alpha = 0.0; if (sumOfMovingMaskValues > 1e-14) { this->m_Alpha = 1.0 / sumOfMovingMaskValues; } const auto numberOfParameters = this->GetNumberOfParameters(); for (unsigned int i = 0; i < numberOfParameters; ++i) { this->m_PerturbedAlphaRight[i] += sumOfMovingMaskValues; this->m_PerturbedAlphaLeft[i] += sumOfMovingMaskValues; if (this->m_PerturbedAlphaRight[i] > 1e-10) { this->m_PerturbedAlphaRight[i] = 1.0 / this->m_PerturbedAlphaRight[i]; } else { this->m_PerturbedAlphaRight[i] = 0.0; } if (this->m_PerturbedAlphaLeft[i] > 1e-10) { this->m_PerturbedAlphaLeft[i] = 1.0 / this->m_PerturbedAlphaLeft[i]; } else { this->m_PerturbedAlphaLeft[i] = 0.0; } } } // end ComputePDFsAndIncrementalPDFs() } // end namespace itk #endif // end #ifndef itkParzenWindowHistogramImageToImageMetric_hxx elastix-5.2.0/Common/CostFunctions/itkScaledSingleValuedCostFunction.cxx000066400000000000000000000170541474534065100265310ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "itkScaledSingleValuedCostFunction.h" #include namespace itk { /** * **************** Constructor ***************************** */ ScaledSingleValuedCostFunction::ScaledSingleValuedCostFunction() = default; /** * ******************** GetValue ***************************** */ ScaledSingleValuedCostFunction::MeasureType ScaledSingleValuedCostFunction::GetValue(const ParametersType & parameters) const { /** F(y)= f(y/s) */ /** This function also checks if the UnscaledCostFunction has been set */ const unsigned int numberOfParameters = this->GetNumberOfParameters(); if (parameters.GetSize() != numberOfParameters) { itkExceptionMacro("Number of parameters is not like the unscaled cost function expects."); } MeasureType returnvalue{}; if (this->m_UseScales) { ParametersType scaledParameters = parameters; this->ConvertScaledToUnscaledParameters(scaledParameters); returnvalue = this->m_UnscaledCostFunction->GetValue(scaledParameters); } else { returnvalue = this->m_UnscaledCostFunction->GetValue(parameters); } if (this->GetNegateCostFunction()) { return -returnvalue; } return returnvalue; } // end GetValue() /** * ******************** GetDerivative ************************** */ void ScaledSingleValuedCostFunction::GetDerivative(const ParametersType & parameters, DerivativeType & derivative) const { /** dF/dy(y)= 1/s * df/dx(y/s) */ /** This function also checks if the UnscaledCostFunction has been set */ const unsigned int numberOfParameters = this->GetNumberOfParameters(); if (parameters.GetSize() != numberOfParameters) { itkExceptionMacro("Number of parameters is not like the unscaled cost function expects."); } if (this->m_UseScales) { ParametersType scaledParameters = parameters; this->ConvertScaledToUnscaledParameters(scaledParameters); this->m_UnscaledCostFunction->GetDerivative(scaledParameters, derivative); const ScalesType & scales = this->GetScales(); for (unsigned int i = 0; i < numberOfParameters; ++i) { derivative[i] /= scales[i]; } } else { m_UnscaledCostFunction->GetDerivative(parameters, derivative); } if (this->GetNegateCostFunction()) { derivative = -derivative; } } // end GetDerivative() /** * **************** GetValueAndDerivative ************************ */ void ScaledSingleValuedCostFunction::GetValueAndDerivative(const ParametersType & parameters, MeasureType & value, DerivativeType & derivative) const { /** F(y)= f(y/s) */ /** dF/dy(y)= 1/s * df/dx(y/s) */ /** This function also checks if the UnscaledCostFunction has been set */ const unsigned int numberOfParameters = this->GetNumberOfParameters(); if (parameters.GetSize() != numberOfParameters) { itkExceptionMacro("Number of parameters is not like the unscaled cost function expects."); } if (this->m_UseScales) { ParametersType scaledParameters = parameters; this->ConvertScaledToUnscaledParameters(scaledParameters); this->m_UnscaledCostFunction->GetValueAndDerivative(scaledParameters, value, derivative); const ScalesType & scales = this->GetScales(); for (unsigned int i = 0; i < numberOfParameters; ++i) { derivative[i] /= scales[i]; } } else { this->m_UnscaledCostFunction->GetValueAndDerivative(parameters, value, derivative); } if (this->GetNegateCostFunction()) { value = -value; derivative = -derivative; } } // end GetValueAndDerivative() /** * **************** GetNumberOfParameters ************************ */ unsigned int ScaledSingleValuedCostFunction::GetNumberOfParameters() const { if (this->m_UnscaledCostFunction.IsNull()) { itkExceptionMacro("UnscaledCostFunction has not been set!"); } return this->m_UnscaledCostFunction->GetNumberOfParameters(); } // end GetNumberOfParameters() /** * **************** SetScales ********************************** */ void ScaledSingleValuedCostFunction::SetScales(const ScalesType & scales) { itkDebugMacro("setting scales to " << scales); this->m_Scales = scales; this->m_SquaredScales.SetSize(scales.GetSize()); for (unsigned int i = 0; i < scales.Size(); ++i) { this->m_SquaredScales[i] = vnl_math::sqr(scales[i]); } this->Modified(); } // end SetScales() /** * **************** SetSquaredScales ***************************** */ void ScaledSingleValuedCostFunction::SetSquaredScales(const ScalesType & squaredScales) { itkDebugMacro("setting squared scales to " << squaredScales); this->m_SquaredScales = squaredScales; this->m_Scales.SetSize(squaredScales.GetSize()); for (unsigned int i = 0; i < squaredScales.Size(); ++i) { this->m_Scales[i] = std::sqrt(squaredScales[i]); } this->Modified(); } // end SetSquaredScales() /** * *************** ConvertScaledToUnscaledParameters ******************** */ void ScaledSingleValuedCostFunction::ConvertScaledToUnscaledParameters(ParametersType & parameters) const { if (this->m_UseScales) { const unsigned int numberOfParameters = parameters.GetSize(); const ScalesType & scales = this->GetScales(); if (scales.GetSize() != numberOfParameters) { itkExceptionMacro("Number of scales is not correct."); } for (unsigned int i = 0; i < numberOfParameters; ++i) { parameters[i] /= scales[i]; } } // end if use scales } // end ConvertScaledToUnscaledParameters() /** * *************** ConvertUnscaledToScaledParameters ******************** */ void ScaledSingleValuedCostFunction::ConvertUnscaledToScaledParameters(ParametersType & parameters) const { if (this->m_UseScales) { const unsigned int numberOfParameters = parameters.GetSize(); const ScalesType & scales = this->GetScales(); if (scales.GetSize() != numberOfParameters) { itkExceptionMacro("Number of scales is not correct."); } for (unsigned int i = 0; i < numberOfParameters; ++i) { parameters[i] *= scales[i]; } } // end if use scales } // end ConvertUnscaledToScaledParameters() /** * *************** PrintSelf ******************** */ void ScaledSingleValuedCostFunction::PrintSelf(std::ostream & os, Indent indent) const { /** Call the superclass' PrintSelf. */ Superclass::PrintSelf(os, indent); os << indent << "UseScales: " << (this->m_UseScales ? "true" : "false") << std::endl; os << indent << "Scales: " << this->m_Scales << std::endl; os << indent << "SquaredScales: " << this->m_SquaredScales << std::endl; os << indent << "NegateCostFunction: " << (this->m_NegateCostFunction ? "true" : "false") << std::endl; os << indent << "UnscaledCostFunction: " << this->m_UnscaledCostFunction.GetPointer() << std::endl; } // end PrintSelf() } // end namespace itk elastix-5.2.0/Common/CostFunctions/itkScaledSingleValuedCostFunction.h000066400000000000000000000126611474534065100261550ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkScaledSingleValuedCostFunction_h #define itkScaledSingleValuedCostFunction_h #include "itkSingleValuedCostFunction.h" #include "itkIntTypes.h" //temp, needed for IdentifierType namespace itk { /** * \class ScaledSingleValuedCostFunction * \brief A cost function that applies a scaling to another cost function * * This class can be used to adapt an existing, badly scaled, cost function. * * By default it does not apply any scaling. Use the method SetUseScales(true) * to enable the use of scales. * * \ingroup Numerics */ class ScaledSingleValuedCostFunction : public SingleValuedCostFunction { public: ITK_DISALLOW_COPY_AND_MOVE(ScaledSingleValuedCostFunction); /** Standard ITK-stuff. */ using Self = ScaledSingleValuedCostFunction; using Superclass = SingleValuedCostFunction; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(ScaledSingleValuedCostFunction, SingleValuedCostFunction); /** Typedefs inherited from the superclass. */ using Superclass::MeasureType; using Superclass::DerivativeType; using Superclass::ParametersType; // temporary, untill it is fixed in the ITK4 // typedef IdentifierType NumberOfParametersType; // temp, copied from itk::TransformBase using NumberOfParametersType = unsigned int; // temp, copied from itk::CostFunction using SingleValuedCostFunctionPointer = Superclass::Pointer; using ScalesType = Array; /** Divide the parameters by the scales and call the GetValue routine * of the unscaled cost function. */ MeasureType GetValue(const ParametersType & parameters) const override; /** Divide the parameters by the scales, call the GetDerivative routine * of the unscaled cost function and divide the resulting derivative by * the scales. */ void GetDerivative(const ParametersType & parameters, DerivativeType & derivative) const override; /** Same procedure as in GetValue and GetDerivative. */ void GetValueAndDerivative(const ParametersType & parameters, MeasureType & value, DerivativeType & derivative) const override; /** Ask the UnscaledCostFunction how many parameters it has. */ NumberOfParametersType GetNumberOfParameters() const override; /** Set the cost function that needs scaling. */ itkSetObjectMacro(UnscaledCostFunction, Superclass); /** Get the cost function that needs scaling. */ itkGetModifiableObjectMacro(UnscaledCostFunction, Superclass); /** Set the scales. Also computes the squared scales, just in case users * call GetSquaredScales (for compatibility with the ITK convention). */ virtual void SetScales(const ScalesType & scales); /** Get the scales. */ itkGetConstReferenceMacro(Scales, ScalesType); /** The ITK convention is to use the squared scales. This function * takes the square root of the input scales and sets them as the * the actual scales */ virtual void SetSquaredScales(const ScalesType & squaredScales); /** The ITK convention is to use the squared scales. This function * returns the squared actual scales. */ itkGetConstReferenceMacro(SquaredScales, ScalesType); /** Set the flag to use scales or not. */ itkSetMacro(UseScales, bool); /** Get the flag to use scales or not. */ itkGetConstMacro(UseScales, bool); /** Set the flag to negate the cost function or not. */ itkBooleanMacro(NegateCostFunction); /** Set the flag to negate the cost function or not. */ itkSetMacro(NegateCostFunction, bool); /** Get the flag to negate the cost function or not. */ itkGetConstMacro(NegateCostFunction, bool); /** Convert the parameters from scaled to unscaled: x = y/s. */ virtual void ConvertScaledToUnscaledParameters(ParametersType & parameters) const; /** Convert the parameters from unscaled to scaled: y = x*s. */ virtual void ConvertUnscaledToScaledParameters(ParametersType & parameters) const; protected: /** The constructor. */ ScaledSingleValuedCostFunction(); /** The destructor. */ ~ScaledSingleValuedCostFunction() override = default; /** PrintSelf. */ void PrintSelf(std::ostream & os, Indent indent) const override; private: /** Member variables. */ ScalesType m_Scales{}; ScalesType m_SquaredScales{}; SingleValuedCostFunctionPointer m_UnscaledCostFunction{ nullptr }; bool m_UseScales{ false }; bool m_NegateCostFunction{ false }; }; } // end namespace itk #endif // #ifndef itkScaledSingleValuedCostFunction_h elastix-5.2.0/Common/CostFunctions/itkSingleValuedPointSetToPointSetMetric.h000066400000000000000000000172211474534065100273220ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ /*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkSingleValuedPointSetToPointSetMetric.h,v $ Date: $Date: 2009-01-26 21:45:56 $ Version: $Revision: 1.2 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef itkSingleValuedPointSetToPointSetMetric_h #define itkSingleValuedPointSetToPointSetMetric_h #include "itkImageBase.h" #include "itkAdvancedTransform.h" #include "itkSingleValuedCostFunction.h" #include "itkMacro.h" #include "itkImageMaskSpatialObject.h" #include "itkPointSet.h" namespace itk { /** \class SingleValuedPointSetToPointSetMetric * \brief Computes similarity between two point sets. * * This Class is templated over the type of the two point-sets. It * expects a Transform to be plugged in. This particular * class is the base class for a hierarchy of point-set to point-set metrics. * * This class computes a value that measures the similarity between the fixed point-set * and the transformed moving point-set. * * \ingroup RegistrationMetrics * */ template class ITK_TEMPLATE_EXPORT SingleValuedPointSetToPointSetMetric : public SingleValuedCostFunction { public: ITK_DISALLOW_COPY_AND_MOVE(SingleValuedPointSetToPointSetMetric); /** Standard class typedefs. */ using Self = SingleValuedPointSetToPointSetMetric; using Superclass = SingleValuedCostFunction; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Type used for representing point components */ using CoordinateRepresentationType = Superclass::ParametersValueType; /** Run-time type information (and related methods). */ itkTypeMacro(SingleValuedPointSetToPointSetMetric, SingleValuedCostFunction); /** Typedefs. */ using FixedPointSetType = TFixedPointSet; using FixedPointSetPixelType = typename FixedPointSetType::PixelType; using FixedPointSetConstPointer = typename FixedPointSetType::ConstPointer; using MovingPointSetType = TMovingPointSet; using MovingPointSetPixelType = typename MovingPointSetType::PixelType; using MovingPointSetConstPointer = typename MovingPointSetType::ConstPointer; using PointIterator = typename FixedPointSetType::PointsContainer::ConstIterator; using PointDataIterator = typename FixedPointSetType::PointDataContainer::ConstIterator; /** Constants for the pointset dimensions. */ itkStaticConstMacro(FixedPointSetDimension, unsigned int, TFixedPointSet::PointDimension); itkStaticConstMacro(MovingPointSetDimension, unsigned int, TMovingPointSet::PointDimension); /** More typedefs. */ using TransformType = AdvancedTransform; using TransformPointer = typename TransformType::Pointer; using InputPointType = typename TransformType::InputPointType; using OutputPointType = typename TransformType::OutputPointType; using TransformParametersType = typename TransformType::ParametersType; using TransformJacobianType = typename TransformType::JacobianType; using FixedImageMaskType = ImageMaskSpatialObject; using FixedImageMaskPointer = typename FixedImageMaskType::Pointer; using FixedImageMaskConstPointer = typename FixedImageMaskType::ConstPointer; using MovingImageMaskType = ImageMaskSpatialObject; using MovingImageMaskPointer = typename MovingImageMaskType::Pointer; using MovingImageMaskConstPointer = typename MovingImageMaskType::ConstPointer; /** Type of the measure. */ using Superclass::MeasureType; using Superclass::DerivativeType; using DerivativeValueType = typename DerivativeType::ValueType; using Superclass::ParametersType; /** Typedefs for support of sparse Jacobians and compact support of transformations. */ using NonZeroJacobianIndicesType = typename TransformType::NonZeroJacobianIndicesType; /** Connect the fixed pointset. */ itkSetConstObjectMacro(FixedPointSet, FixedPointSetType); /** Get the fixed pointset. */ itkGetConstObjectMacro(FixedPointSet, FixedPointSetType); /** Connect the moving pointset. */ itkSetConstObjectMacro(MovingPointSet, MovingPointSetType); /** Get the moving pointset. */ itkGetConstObjectMacro(MovingPointSet, MovingPointSetType); /** Connect the Transform. */ itkSetObjectMacro(Transform, TransformType); /** Get a pointer to the Transform. */ itkGetConstObjectMacro(Transform, TransformType); /** Set the parameters defining the Transform. */ void SetTransformParameters(const ParametersType & parameters) const; /** Return the number of parameters required by the transform. */ unsigned int GetNumberOfParameters() const override { return this->m_Transform->GetNumberOfParameters(); } /** Initialize the Metric by making sure that all the components are * present and plugged together correctly. */ virtual void Initialize(); /** Set the fixed mask. */ // \todo: currently not used itkSetConstObjectMacro(FixedImageMask, FixedImageMaskType); /** Get the fixed mask. */ itkGetConstObjectMacro(FixedImageMask, FixedImageMaskType); /** Set the moving mask. */ itkSetConstObjectMacro(MovingImageMask, MovingImageMaskType); /** Get the moving mask. */ itkGetConstObjectMacro(MovingImageMask, MovingImageMaskType); /** Contains calls from GetValueAndDerivative that are thread-unsafe. */ virtual void BeforeThreadedGetValueAndDerivative(const TransformParametersType & parameters) const; /** Switch the function BeforeThreadedGetValueAndDerivative on or off. */ itkSetMacro(UseMetricSingleThreaded, bool); itkGetConstReferenceMacro(UseMetricSingleThreaded, bool); itkBooleanMacro(UseMetricSingleThreaded); protected: SingleValuedPointSetToPointSetMetric() = default; ~SingleValuedPointSetToPointSetMetric() override = default; /** PrintSelf. */ void PrintSelf(std::ostream & os, Indent indent) const override; /** Member variables. */ FixedPointSetConstPointer m_FixedPointSet{ nullptr }; MovingPointSetConstPointer m_MovingPointSet{ nullptr }; FixedImageMaskConstPointer m_FixedImageMask{ nullptr }; MovingImageMaskConstPointer m_MovingImageMask{ nullptr }; mutable TransformPointer m_Transform{ nullptr }; mutable unsigned int m_NumberOfPointsCounted{ 0 }; /** Variables for multi-threading. */ bool m_UseMetricSingleThreaded{ true }; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkSingleValuedPointSetToPointSetMetric.hxx" #endif #endif elastix-5.2.0/Common/CostFunctions/itkSingleValuedPointSetToPointSetMetric.hxx000066400000000000000000000101041474534065100276730ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ /*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile: itkSingleValuedPointSetToPointSetMetric.txx,v $ Date: $Date: 2009-01-26 21:45:56 $ Version: $Revision: 1.2 $ Copyright (c) Insight Software Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef itkSingleValuedPointSetToPointSetMetric_hxx #define itkSingleValuedPointSetToPointSetMetric_hxx #include "itkSingleValuedPointSetToPointSetMetric.h" namespace itk { /** * ******************* SetTransformParameters *********************** */ template void SingleValuedPointSetToPointSetMetric::SetTransformParameters( const ParametersType & parameters) const { if (!this->m_Transform) { itkExceptionMacro("Transform has not been assigned"); } this->m_Transform->SetParameters(parameters); } // end SetTransformParameters() /** * ******************* Initialize *********************** */ template void SingleValuedPointSetToPointSetMetric::Initialize() { if (!this->m_Transform) { itkExceptionMacro("Transform is not present"); } if (!this->m_MovingPointSet) { itkExceptionMacro("MovingPointSet is not present"); } if (!this->m_FixedPointSet) { itkExceptionMacro("FixedPointSet is not present"); } // If the PointSet is provided by a source, update the source. this->m_MovingPointSet->UpdateSource(); // If the point set is provided by a source, update the source. this->m_FixedPointSet->UpdateSource(); } // end Initialize() /** * *********************** BeforeThreadedGetValueAndDerivative *********************** */ template void SingleValuedPointSetToPointSetMetric::BeforeThreadedGetValueAndDerivative( const TransformParametersType & parameters) const { /** In this function do all stuff that cannot be multi-threaded. */ if (this->m_UseMetricSingleThreaded) { this->SetTransformParameters(parameters); } } // end BeforeThreadedGetValueAndDerivative() /** * ******************* PrintSelf *********************** */ template void SingleValuedPointSetToPointSetMetric::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << "Fixed PointSet: " << this->m_FixedPointSet.GetPointer() << std::endl; os << "Moving PointSet: " << this->m_MovingPointSet.GetPointer() << std::endl; os << "Fixed mask: " << this->m_FixedImageMask.GetPointer() << std::endl; os << "Moving mask: " << this->m_MovingImageMask.GetPointer() << std::endl; os << "Transform: " << this->m_Transform.GetPointer() << std::endl; } // end PrintSelf() } // end namespace itk #endif // end #ifndef itkSingleValuedPointSetToPointSetMetric_hxx elastix-5.2.0/Common/CostFunctions/itkTransformPenaltyTerm.h000066400000000000000000000134741474534065100242630ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkTransformPenaltyTerm_h #define itkTransformPenaltyTerm_h #include "itkAdvancedImageToImageMetric.h" // Needed for checking for B-spline for faster implementation #include "itkAdvancedBSplineDeformableTransform.h" #include "itkAdvancedCombinationTransform.h" namespace itk { /** * \class TransformPenaltyTerm * \brief A cost function that calculates a penalty term * on a transformation. * * We decided to make it an itk::ImageToImageMetric, since possibly * all the stuff in there is also needed for penalty terms. * * A transformation penalty terms has some extra demands on the transform. * Therefore, the transformation is required to be of itk::AdvancedTransform * type. * * \ingroup Metrics */ template class ITK_TEMPLATE_EXPORT TransformPenaltyTerm : public AdvancedImageToImageMetric { public: ITK_DISALLOW_COPY_AND_MOVE(TransformPenaltyTerm); /** Standard ITK stuff. */ using Self = TransformPenaltyTerm; using Superclass = AdvancedImageToImageMetric; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Run-time type information (and related methods). */ itkTypeMacro(TransformPenaltyTerm, AdvancedImageToImageMetric); /** Typedef's inherited from the superclass. */ using typename Superclass::CoordinateRepresentationType; using typename Superclass::MovingImageType; using typename Superclass::MovingImagePixelType; using typename Superclass::MovingImagePointer; using typename Superclass::MovingImageConstPointer; using typename Superclass::FixedImageType; using typename Superclass::FixedImagePointer; using typename Superclass::FixedImageConstPointer; using typename Superclass::FixedImageRegionType; // these not: use advanced transform below // using typename Superclass::TransformType; // using typename Superclass::TransformPointer; using typename Superclass::InputPointType; using typename Superclass::OutputPointType; using typename Superclass::TransformParametersType; using typename Superclass::TransformJacobianType; using typename Superclass::InterpolatorType; using typename Superclass::InterpolatorPointer; using typename Superclass::RealType; using typename Superclass::GradientPixelType; using typename Superclass::GradientImageType; using typename Superclass::GradientImagePointer; using typename Superclass::FixedImageMaskType; using typename Superclass::FixedImageMaskPointer; using typename Superclass::MovingImageMaskType; using typename Superclass::MovingImageMaskPointer; using typename Superclass::MeasureType; using typename Superclass::DerivativeType; using typename Superclass::DerivativeValueType; using typename Superclass::ParametersType; using typename Superclass::FixedImagePixelType; using typename Superclass::ImageSampleContainerType; using typename Superclass::ImageSampleContainerPointer; using typename Superclass::ThreadInfoType; /** Typedef's for the B-spline transform. */ using typename Superclass::CombinationTransformType; using typename Superclass::BSplineOrder1TransformType; using typename Superclass::BSplineOrder1TransformPointer; using typename Superclass::BSplineOrder2TransformType; using typename Superclass::BSplineOrder2TransformPointer; using typename Superclass::BSplineOrder3TransformType; using typename Superclass::BSplineOrder3TransformPointer; /** Template parameters. FixedImageType has already been taken from superclass. */ using ScalarType = TScalarType; // \todo: not really meaningful name. /** Typedefs from the AdvancedTransform. */ using TransformType = typename Superclass::AdvancedTransformType; using SpatialJacobianType = typename TransformType::SpatialJacobianType; using JacobianOfSpatialJacobianType = typename TransformType::JacobianOfSpatialJacobianType; using SpatialHessianType = typename TransformType::SpatialHessianType; using JacobianOfSpatialHessianType = typename TransformType::JacobianOfSpatialHessianType; using InternalMatrixType = typename TransformType::InternalMatrixType; /** Define the dimension. */ itkStaticConstMacro(FixedImageDimension, unsigned int, FixedImageType::ImageDimension); protected: /** Typedefs for indices and points. */ using typename Superclass::FixedImageIndexType; using typename Superclass::FixedImageIndexValueType; using typename Superclass::MovingImageIndexType; using typename Superclass::FixedImagePointType; using typename Superclass::MovingImagePointType; using typename Superclass::MovingImageContinuousIndexType; using typename Superclass::NonZeroJacobianIndicesType; /** The constructor. */ TransformPenaltyTerm() = default; /** The destructor. */ ~TransformPenaltyTerm() override = default; /** A function to check if the transform is B-spline, for speedup. */ virtual bool CheckForBSplineTransform2(BSplineOrder3TransformPointer & bspline) const; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkTransformPenaltyTerm.hxx" #endif #endif // #ifndef itkTransformPenaltyTerm_h elastix-5.2.0/Common/CostFunctions/itkTransformPenaltyTerm.hxx000066400000000000000000000045001474534065100246310ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkTransformPenaltyTerm_hxx #define itkTransformPenaltyTerm_hxx #include "itkTransformPenaltyTerm.h" namespace itk { /** * ****************** CheckForBSplineTransform ******************************* */ template bool TransformPenaltyTerm::CheckForBSplineTransform2(BSplineOrder3TransformPointer & bspline) const { /** The following checks for many spline orders. */ this->CheckForBSplineTransform(); /** Quit if the advanced transform is not a B-spline. */ if (!Superclass::m_TransformIsBSpline) return false; /** We will return the B-spline by reference, but only in case it is a third order B-spline. */ BSplineOrder3TransformType * testPtr1 = dynamic_cast(Superclass::m_AdvancedTransform.GetPointer()); CombinationTransformType * testPtr2a = dynamic_cast(Superclass::m_AdvancedTransform.GetPointer()); if (testPtr1) { /** The transform is of type AdvancedBSplineDeformableTransform. */ bspline = testPtr1; } else if (testPtr2a) { /** The transform is of type AdvancedCombinationTransform. */ BSplineOrder3TransformType * testPtr2b = dynamic_cast((testPtr2a->GetModifiableCurrentTransform())); if (testPtr2b) { /** The current transform is of type AdvancedBSplineDeformableTransform. */ bspline = testPtr2b; } } return true; } // end CheckForBSplineTransform() } // end namespace itk #endif // #ifndef itkTransformPenaltyTerm_hxx elastix-5.2.0/Common/GTesting/000077500000000000000000000000001474534065100161545ustar00rootroot00000000000000elastix-5.2.0/Common/GTesting/CMakeLists.txt000066400000000000000000000014751474534065100207230ustar00rootroot00000000000000add_executable(CommonGTest elxConversionGTest.cxx elxDefaultConstructGTest.cxx elxElastixMainGTest.cxx elxGTestUtilities.h elxResampleInterpolatorGTest.cxx elxResamplerGTest.cxx elxTransformIOGTest.cxx itkAdvancedImageToImageMetricGTest.cxx itkAdvancedMeanSquaresImageToImageMetricGTest.cxx itkComputeImageExtremaFilterGTest.cxx itkImageFullSamplerGTest.cxx itkImageGridSamplerGTest.cxx itkImageRandomCoordinateSamplerGTest.cxx itkImageRandomSamplerGTest.cxx itkImageRandomSamplerSparseMaskGTest.cxx itkImageSamplerGTest.cxx itkParameterMapInterfaceTest.cxx ) target_compile_definitions(CommonGTest PRIVATE _USE_MATH_DEFINES # For M_PI. ) target_link_libraries(CommonGTest GTest::GTest GTest::Main ${ITK_LIBRARIES} elastix_lib ) add_test(NAME CommonGTest_test COMMAND CommonGTest) elastix-5.2.0/Common/GTesting/elxConversionGTest.cxx000066400000000000000000000620611474534065100225120ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ // First include the header file to be tested: #include "elxConversion.h" #include "elxDefaultConstruct.h" #include "itkParameterFileParser.h" #include "itkParameterMapInterface.h" #include "../Core/Main/GTesting/elxCoreMainGTestUtilities.h" #include #include #include #include // For iota. #include #include // For is_floating_point. #include #include #include #include #include #include #include // The class to be tested. using elastix::Conversion; // Using-declaration: using elx::CoreMainGTestUtilities::CheckNew; using ParameterMapType = itk::ParameterMapInterface::ParameterMapType; namespace { // A realistic test example of a parameter map and its text string representation: namespace TestExample { const ParameterMapType parameterMap = { { "Direction", { "0", "0", "0", "0" } }, { "FixedImageDimension", { "2" } }, { "FixedInternalImagePixelType", { "float" } }, { "HowToCombineTransforms", { "Compose" } }, { "Index", { "0", "0" } }, { "InitialTransformParameterFileName", { "NoInitialTransform" } }, { "MovingImageDimension", { "2" } }, { "MovingInternalImagePixelType", { "float" } }, { "NumberOfParameters", { "2" } }, { "Origin", { "0", "0" } }, { "Size", { "0", "0" } }, { "Spacing", { "1", "1" } }, { "Transform", { "TranslationTransform" } }, { "TransformParameters", { "0", "0" } }, { "UseDirectionCosines", { "true" } } }; const std::string parameterMapTextString = R"((Direction 0 0 0 0) (FixedImageDimension 2) (FixedInternalImagePixelType "float") (HowToCombineTransforms "Compose") (Index 0 0) (InitialTransformParameterFileName "NoInitialTransform") (MovingImageDimension 2) (MovingInternalImagePixelType "float") (NumberOfParameters 2) (Origin 0 0) (Size 0 0) (Spacing 1 1) (Transform "TranslationTransform") (TransformParameters 0 0) (UseDirectionCosines "true") )"; } // namespace TestExample template TParameterValue Expect_successful_round_trip_of_parameter_value(const TParameterValue & parameterValue) { const std::string parameterName("Key"); const itk::ParameterMapInterface::ParameterMapType parameterMap{ { parameterName, { Conversion::ToString(parameterValue) } } }; const auto parameterMapInterface = CheckNew(); parameterMapInterface->SetParameterMap(parameterMap); TParameterValue actualParameterValue{}; std::string errorMessage; try { EXPECT_TRUE(parameterMapInterface->ReadParameter(actualParameterValue, parameterName, 0, errorMessage)); } catch (const itk::ExceptionObject & exceptionObject) { EXPECT_EQ(exceptionObject.what(), std::string{}); } EXPECT_EQ(errorMessage, std::string{}); return actualParameterValue; } template void Expect_lossless_round_trip_of_parameter_value(const TParameterValue & parameterValue) { const TParameterValue & roundTrippedParameterValue = Expect_successful_round_trip_of_parameter_value(parameterValue); EXPECT_EQ(roundTrippedParameterValue, parameterValue); } template void Expect_lossless_round_trip_of_parameter_values(const std::initializer_list & parameterValues) { for (const auto parameterValue : parameterValues) { Expect_lossless_round_trip_of_parameter_value(parameterValue); } } template void Expect_lossless_round_trip_of_positive_and_negative_parameter_values( const std::initializer_list & parameterValues) { for (const auto parameterValue : parameterValues) { Expect_lossless_round_trip_of_parameter_value(+parameterValue); Expect_lossless_round_trip_of_parameter_value(-parameterValue); } } template void Expect_lossless_round_trip_of_unsigned_parameter_values() { using NumericLimits = std::numeric_limits; static_assert(!NumericLimits::is_signed, "This function is only meant to test unsigned types!"); // Note: the static_cast for `NumericLimits::max() - 1` is a workaround for // the following compile error from Visual C++ 2017: // > error C2398: Element '3': conversion from 'int' to 'TUnsignedInteger' requires a narrowing conversion Expect_lossless_round_trip_of_parameter_values( { 0, 1, static_cast(NumericLimits::max() - 1), NumericLimits::max() }); } template void Expect_lossless_round_trip_of_signed_integer_parameter_values() { using NumericLimits = std::numeric_limits; static_assert(NumericLimits::is_signed && NumericLimits::is_integer, "This function is only meant to test signed integer types!"); const auto minValue = NumericLimits::min(); const auto maxValue = NumericLimits::max(); Expect_lossless_round_trip_of_parameter_values( { minValue, minValue + 1, -1, 0, 1, maxValue - 1, maxValue }); } template void Expect_parameter_with_decimal_point_and_trailing_zeros_can_be_read_as_integer() { using NumericLimits = std::numeric_limits; const auto minValue = NumericLimits::min(); const auto maxValue = NumericLimits::max(); for (const TInteger integer : { minValue, static_cast(minValue + 1), TInteger{}, TInteger{ 1 }, static_cast(maxValue - 1), maxValue }) { for (unsigned numberOfTrailingZeros{}; numberOfTrailingZeros <= 2; ++numberOfTrailingZeros) { const std::string parameterName("Key"); const std::string parameterStringValue = std::to_string(integer) + '.' + std::string(numberOfTrailingZeros, '0'); const auto parameterMapInterface = CheckNew(); parameterMapInterface->SetParameterMap({ { parameterName, { parameterStringValue } } }); TInteger actualParameterValue{}; std::string errorMessage; EXPECT_TRUE(parameterMapInterface->ReadParameter(actualParameterValue, parameterName, 0, errorMessage)); EXPECT_EQ(errorMessage, std::string{}); EXPECT_EQ(actualParameterValue, integer); } } } template void Expect_parameter_with_decimal_point_and_non_zero_trailing_chars_can_not_be_read_as_integer() { using NumericLimits = std::numeric_limits; for (const TInteger integer : { NumericLimits::min(), TInteger{}, TInteger{ 1 }, NumericLimits::max() }) { for (const auto trail : { " ", " 0", "1", "10", "A", "F" }) { const std::string parameterName("Key"); const std::string parameterStringValue = std::to_string(integer) + '.' + trail; const auto parameterMapInterface = CheckNew(); parameterMapInterface->SetParameterMap({ { parameterName, { parameterStringValue } } }); TInteger actualParameterValue{}; std::string errorMessage; EXPECT_THROW(parameterMapInterface->ReadParameter(actualParameterValue, parameterName, 0, errorMessage), itk::ExceptionObject); } } } template void Expect_negative_parameter_value_can_not_be_read_as_unsigned() { static_assert(std::is_signed_v, "Must be a signed integer type!"); for (TSignedInteger integer : { std::numeric_limits::min(), TSignedInteger{ -1 } }) { const std::string parameterName("Key"); const std::string parameterStringValue = std::to_string(integer); elx::DefaultConstruct parameterMapInterface; parameterMapInterface.SetParameterMap({ { parameterName, { parameterStringValue } } }); std::make_unsigned_t actualParameterValue{}; std::string errorMessage; EXPECT_THROW(parameterMapInterface.ReadParameter(actualParameterValue, parameterName, 0, errorMessage), itk::ExceptionObject); } } template void Expect_lossless_round_trip_of_floating_point_parameter_values() { static_assert(std::is_floating_point_v, "This function is only meant to test floating point types!"); using NumericLimits = std::numeric_limits; Expect_lossless_round_trip_of_positive_and_negative_parameter_values({ 0, NumericLimits::denorm_min(), NumericLimits::min(), NumericLimits::epsilon(), 1, NumericLimits::max(), NumericLimits::infinity() }); const TFloatingPoint & roundTrippedNaN = Expect_successful_round_trip_of_parameter_value(NumericLimits::quiet_NaN()); EXPECT_TRUE(std::isnan(roundTrippedNaN)); } } // namespace GTEST_TEST(Conversion, BoolToString) { // Tests that BoolToString can be evaluated at compile-time. static_assert((Conversion::BoolToString(false) != nullptr) && (Conversion::BoolToString(true) != nullptr), "BoolToString(bool) does not return nullptr"); EXPECT_EQ(Conversion::BoolToString(false), std::string{ "false" }); EXPECT_EQ(Conversion::BoolToString(true), std::string{ "true" }); } GTEST_TEST(Conversion, ToOptimizerParameters) { using OptimizerParametersType = itk::OptimizerParameters; using StdVectorType = std::vector; EXPECT_EQ(Conversion::ToOptimizerParameters(StdVectorType{}), OptimizerParametersType{}); // Sanity check: _not_ just every result from ToOptimizerParameters compares equal to any OptimizerParameters object! EXPECT_NE(Conversion::ToOptimizerParameters(StdVectorType{ 0.0 }), OptimizerParametersType{}); for (const double value : { -1.0, 0.0, 1.0, DBL_MIN, DBL_MAX }) { EXPECT_EQ(Conversion::ToOptimizerParameters(StdVectorType{ value }), OptimizerParametersType(1U, value)); } StdVectorType stdVector(10U); std::iota(stdVector.begin(), stdVector.end(), 1.0); const auto optimizerParameters = Conversion::ToOptimizerParameters(stdVector); ASSERT_EQ(optimizerParameters.size(), stdVector.size()); EXPECT_TRUE(std::equal(optimizerParameters.begin(), optimizerParameters.end(), stdVector.cbegin())); } GTEST_TEST(Conversion, ParameterMapToString) { EXPECT_EQ(Conversion::ParameterMapToString({}), std::string{}); EXPECT_EQ(Conversion::ParameterMapToString({ { "A", {} } }), "(A)\n"); EXPECT_EQ(Conversion::ParameterMapToString({ { "Numbers", { "0", "1" } } }), "(Numbers 0 1)\n"); EXPECT_EQ(Conversion::ParameterMapToString({ { "Letters", { "a", "z" } } }), "(Letters \"a\" \"z\")\n"); EXPECT_EQ(Conversion::ParameterMapToString(TestExample::parameterMap), TestExample::parameterMapTextString); } GTEST_TEST(ParameterFileParser, ConvertTextToParameterMap) { using itk::ParameterFileParser; EXPECT_EQ(ParameterFileParser::ConvertToParameterMap(""), ParameterMapType{}); EXPECT_EQ(ParameterFileParser::ConvertToParameterMap("(Numbers 0 1)\n"), ParameterMapType({ { "Numbers", { "0", "1" } } })); EXPECT_EQ(ParameterFileParser::ConvertToParameterMap("(Letters \"a\" \"z\")\n"), ParameterMapType({ { "Letters", { "a", "z" } } })); EXPECT_EQ(ParameterFileParser::ConvertToParameterMap(TestExample::parameterMapTextString), TestExample::parameterMap); } GTEST_TEST(Conversion, ToString) { // Note that this is different from std::to_string(false) and // std::to_string(true), which return "0" and "1", respecively. EXPECT_EQ(Conversion::ToString(false), "false"); EXPECT_EQ(Conversion::ToString(true), "true"); EXPECT_EQ(Conversion::ToString(0), "0"); EXPECT_EQ(Conversion::ToString(1), "1"); EXPECT_EQ(Conversion::ToString(-1), "-1"); EXPECT_EQ(Conversion::ToString(char{}), "0"); EXPECT_EQ(Conversion::ToString(char{ 2 }), "2"); EXPECT_EQ(Conversion::ToString(std::numeric_limits::min()), "-128"); EXPECT_EQ(Conversion::ToString(std::numeric_limits::max()), "255"); EXPECT_EQ(Conversion::ToString(std::numeric_limits::min()), "-9223372036854775808"); EXPECT_EQ(Conversion::ToString(std::numeric_limits::max()), "18446744073709551615"); // Extensive tests of conversion from double to string: using DoubleLimits = std::numeric_limits; // Note that this is different from std::to_string(0.5), which returns "0.500000" EXPECT_EQ(Conversion::ToString(0.5), "0.5"); static constexpr auto expectedPrecision = 16; static_assert(expectedPrecision == std::numeric_limits::digits10 + 1, "The expected precision for double floating point numbers"); const auto expectedString = "0." + std::string(expectedPrecision, '3'); EXPECT_EQ(Conversion::ToString(+0.0), "0"); EXPECT_EQ(Conversion::ToString(-0.0), "0"); EXPECT_EQ(Conversion::ToString(+1.0), "1"); EXPECT_EQ(Conversion::ToString(-1.0), "-1"); EXPECT_EQ(Conversion::ToString(0.1), "0.1"); EXPECT_EQ(Conversion::ToString(1.0 / 3.0), "0." + std::string(expectedPrecision, '3')); for (std::uint8_t exponent{ 20 }; exponent > 0; --exponent) { const auto power_of_ten = std::pow(10.0, exponent); // Test +/- 1000...000 EXPECT_EQ(Conversion::ToString(power_of_ten), '1' + std::string(exponent, '0')); EXPECT_EQ(Conversion::ToString(-power_of_ten), "-1" + std::string(exponent, '0')); } for (std::uint8_t exponent{ 15 }; exponent > 0; --exponent) { const auto power_of_ten = std::pow(10.0, exponent); // Test +/- 999...999 EXPECT_EQ(Conversion::ToString(power_of_ten - 1), std::string(exponent, '9')); EXPECT_EQ(Conversion::ToString(1 - power_of_ten), '-' + std::string(exponent, '9')); } for (std::int8_t exponent{ -6 }; exponent < 0; ++exponent) { const auto power_of_ten = std::pow(10.0, exponent); // Test +/- 0.000...001 EXPECT_EQ(Conversion::ToString(power_of_ten), "0." + std::string(-1 - exponent, '0') + '1'); EXPECT_EQ(Conversion::ToString(-power_of_ten), "-0." + std::string(-1 - exponent, '0') + '1'); } for (std::int8_t exponent{ -16 }; exponent < 0; ++exponent) { const auto power_of_ten = std::pow(10.0, exponent); // Test +/- 0.999...999 EXPECT_EQ(Conversion::ToString(1 - power_of_ten), "0." + std::string(-exponent, '9')); EXPECT_EQ(Conversion::ToString(power_of_ten - 1), "-0." + std::string(-exponent, '9')); } // The first powers of ten that are represented by scientific "e" notation: EXPECT_EQ(Conversion::ToString(1e+21), "1e+21"); EXPECT_EQ(Conversion::ToString(1e-7), "1e-7"); // Test the most relevant constants from : EXPECT_EQ(Conversion::ToString(DoubleLimits::epsilon()), "2.220446049250313e-16"); EXPECT_EQ(Conversion::ToString(DoubleLimits::min()), "2.2250738585072014e-308"); EXPECT_EQ(Conversion::ToString(DoubleLimits::lowest()), "-1.7976931348623157e+308"); EXPECT_EQ(Conversion::ToString(DoubleLimits::max()), "1.7976931348623157e+308"); EXPECT_EQ(Conversion::ToString(DoubleLimits::quiet_NaN()), "NaN"); EXPECT_EQ(Conversion::ToString(-DoubleLimits::quiet_NaN()), "NaN"); EXPECT_EQ(Conversion::ToString(DoubleLimits::infinity()), "Infinity"); EXPECT_EQ(Conversion::ToString(-DoubleLimits::infinity()), "-Infinity"); EXPECT_EQ(Conversion::ToString(DoubleLimits::denorm_min()), "5e-324"); EXPECT_EQ(Conversion::ToString(-DoubleLimits::denorm_min()), "-5e-324"); // Even though the IEEE float and double representations of 0.1 both have a // small rounding error, we would rather not have 0.1 converted to a string // like "0.10000000000000001", or even "0.10000000149011612". EXPECT_EQ(Conversion::ToString(0.1), "0.1"); EXPECT_EQ(Conversion::ToString(0.1f), "0.1"); } // Tests that conversion of itk::Object pointers to string and back is lossless. GTEST_TEST(Conversion, LosslessRoundTripOfObjectPointers) { const auto expectLosslessRoundTrip = [](itk::Object * const ptr) { itk::Object * actualPtr{}; EXPECT_TRUE(Conversion::StringToValue(Conversion::ObjectPtrToString(ptr), actualPtr)); EXPECT_EQ(actualPtr, ptr); }; expectLosslessRoundTrip(nullptr); expectLosslessRoundTrip(itk::Object::New()); elx::DefaultConstruct localVariable{}; expectLosslessRoundTrip(&localVariable); } GTEST_TEST(Conversion, ToVectorOfStrings) { using VectorOfStrings = std::vector; using ArrayOfDoubles = std::array; EXPECT_EQ(Conversion::ToVectorOfStrings(itk::Size<>{ 1, 2 }), VectorOfStrings({ "1", "2" })); EXPECT_EQ(Conversion::ToVectorOfStrings(itk::Index<>{ 1, 2 }), VectorOfStrings({ "1", "2" })); EXPECT_EQ(Conversion::ToVectorOfStrings(itk::Point(ArrayOfDoubles{ -0.5, 0.0, 0.25 })), VectorOfStrings({ "-0.5", "0", "0.25" })); EXPECT_EQ(Conversion::ToVectorOfStrings(itk::Vector(ArrayOfDoubles{ -0.5, 0.0, 0.25 }.data())), VectorOfStrings({ "-0.5", "0", "0.25" })); EXPECT_EQ(Conversion::ToVectorOfStrings(itk::OptimizerParameters{}), VectorOfStrings{}); EXPECT_EQ(Conversion::ToVectorOfStrings(itk::ImageBase<>::DirectionType{}), VectorOfStrings({ "0", "0", "0", "0" })); } GTEST_TEST(Conversion, ConcatenateVectors) { using VectorType = std::vector; const auto expectConcatenation = [](const VectorType & vector1, const VectorType & vector2) { auto expectedResult = vector1; for (const auto & element : vector2) { expectedResult.push_back(element); } EXPECT_EQ(Conversion::ConcatenateVectors(vector1, vector2), expectedResult); }; expectConcatenation({}, {}); expectConcatenation({ "1" }, {}); expectConcatenation({}, { "1" }); expectConcatenation({ "1" }, { "2", "3" }); } GTEST_TEST(Conversion, IsNumberReturnsFalseOnNonNumericString) { const auto expect_IsNumber_returns_false = [](const std::string & str) { SCOPED_TRACE(str); EXPECT_FALSE(Conversion::IsNumber(str)); }; for (const auto str : { "", " ", "a", "A B C", "true", "false", "nan", "NaN" }) { expect_IsNumber_returns_false(str); } for (const auto transformName : { "AffineDTITransform", "AffineLogStackTransform", "AffineLogTransform", "AffineTransform", "BSplineStackTransform", "BSplineTransform", "BSplineTransformWithDiffusion", "DeformationFieldTransform", "EulerStackTransform", "EulerTransform", "MultiBSplineTransformWithNormal", "RecursiveBSplineTransform", "SimilarityTransform", "SplineKernelTransform", "TranslationStackTransform", "TranslationTransform", "WeightedCombinationTransform" }) { expect_IsNumber_returns_false(transformName); } } GTEST_TEST(Conversion, IsNumberReturnsTrueOnNumericString) { const auto expect_IsNumber_returns_true = [](const std::string & str) { SCOPED_TRACE(str); EXPECT_TRUE(Conversion::IsNumber(str)); }; for (int i{ -10 }; i < 10; ++i) { expect_IsNumber_returns_true(Conversion::ToString(i)); } expect_IsNumber_returns_true("1e+21"); expect_IsNumber_returns_true("1e-7"); for (double d{ -1.0 }; d < 1.0; d += 0.1) { expect_IsNumber_returns_true(Conversion::ToString(d)); } using limits = std::numeric_limits; for (const auto limit : { limits::min(), limits::max(), limits::epsilon(), limits::lowest(), limits::denorm_min() }) { expect_IsNumber_returns_true(Conversion::ToString(limit)); } } GTEST_TEST(Conversion, LosslessRoundTripOfParameterValue) { Expect_lossless_round_trip_of_unsigned_parameter_values(); Expect_lossless_round_trip_of_unsigned_parameter_values(); Expect_lossless_round_trip_of_unsigned_parameter_values(); Expect_lossless_round_trip_of_unsigned_parameter_values(); Expect_lossless_round_trip_of_signed_integer_parameter_values(); Expect_lossless_round_trip_of_signed_integer_parameter_values(); Expect_lossless_round_trip_of_signed_integer_parameter_values(); Expect_lossless_round_trip_of_signed_integer_parameter_values(); // Note that the C++ Standard (C++11) does not specify whether `char` is a // signed or an unsigned type, so it is tested here separately from // `signed char` (int8_t) and `unsigned char` (uint8_t). for (const char parameterValue : { std::numeric_limits::min(), '\0', '\1', std::numeric_limits::max() }) { Expect_lossless_round_trip_of_parameter_value(parameterValue); } Expect_lossless_round_trip_of_floating_point_parameter_values(); Expect_lossless_round_trip_of_floating_point_parameter_values(); for (const bool parameterValue : { false, true }) { Expect_lossless_round_trip_of_parameter_value(parameterValue); } } GTEST_TEST(Conversion, ToNativePathNameSeparators) { EXPECT_EQ(Conversion::ToNativePathNameSeparators(std::string{}), std::string{}); EXPECT_EQ(Conversion::ToNativePathNameSeparators(" "), std::string{ " " }); static constexpr bool isBackslashNativeSeparator = #ifdef _WIN32 true; #else false; #endif static constexpr char nativeSeparator = isBackslashNativeSeparator ? '\\' : '/'; static constexpr char nonNativeSeparator = isBackslashNativeSeparator ? '/' : '\\'; EXPECT_EQ(Conversion::ToNativePathNameSeparators({ nativeSeparator }), std::string{ nativeSeparator }); EXPECT_EQ(Conversion::ToNativePathNameSeparators({ nonNativeSeparator }), std::string{ nativeSeparator }); EXPECT_EQ(Conversion::ToNativePathNameSeparators({ nonNativeSeparator, nativeSeparator }), std::string(std::size_t{ 2 }, nativeSeparator)); EXPECT_EQ(Conversion::ToNativePathNameSeparators({ nativeSeparator, nonNativeSeparator }), std::string(std::size_t{ 2 }, nativeSeparator)); } GTEST_TEST(ParameterMapInterface, ParameterWithDecimalPointAndTrailingZerosCanBeReadAsInteger) { Expect_parameter_with_decimal_point_and_trailing_zeros_can_be_read_as_integer(); Expect_parameter_with_decimal_point_and_trailing_zeros_can_be_read_as_integer(); Expect_parameter_with_decimal_point_and_trailing_zeros_can_be_read_as_integer(); Expect_parameter_with_decimal_point_and_trailing_zeros_can_be_read_as_integer(); Expect_parameter_with_decimal_point_and_trailing_zeros_can_be_read_as_integer(); Expect_parameter_with_decimal_point_and_trailing_zeros_can_be_read_as_integer(); Expect_parameter_with_decimal_point_and_trailing_zeros_can_be_read_as_integer(); } GTEST_TEST(ParameterMapInterface, ParameterWithDecimalPointAndNonZeroTrailingCharsCanNotBeReadAsInteger) { Expect_parameter_with_decimal_point_and_non_zero_trailing_chars_can_not_be_read_as_integer(); } GTEST_TEST(ParameterMapInterface, NegativeParameterValueCannotNotBeReadAsUnsigned) { Expect_negative_parameter_value_can_not_be_read_as_unsigned(); Expect_negative_parameter_value_can_not_be_read_as_unsigned(); Expect_negative_parameter_value_can_not_be_read_as_unsigned(); } elastix-5.2.0/Common/GTesting/elxDefaultConstructGTest.cxx000066400000000000000000000047341474534065100236610ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ // First include the header file to be tested: #include "elxDefaultConstruct.h" #include #include #include // For is_base_of and is_default_constructible. // The class template to be tested: using elastix::DefaultConstruct; // Example type, to be used as template argument of DefaultConstruct. using ImageType = itk::Image; namespace { // A minimal test class, to be used as template argument of DefaultConstruct. class TestObject : public itk::LightObject { public: ITK_DISALLOW_COPY_AND_MOVE(TestObject); using Self = TestObject; using Superclass = itk::LightObject; using Pointer = itk::SmartPointer; itkNewMacro(Self); protected: TestObject() = default; ~TestObject() = default; private: int m_data{}; friend bool operator==(const Self & lhs, const Self & rhs) { return lhs.m_data == rhs.m_data; } friend bool operator!=(const Self & lhs, const Self & rhs) { return !(lhs == rhs); } }; } // namespace static_assert(std::is_base_of>{} && std::is_base_of>{}, "DefaultConstruct must be a subclass of T! "); static_assert(std::is_default_constructible>{} && std::is_default_constructible>{}, "DefaultConstruct must be default-constructible! "); GTEST_TEST(DefaultConstruct, Check) { const DefaultConstruct defaultConstructedImage{}; const DefaultConstruct defaultConstructedTestObject{}; EXPECT_EQ(defaultConstructedTestObject, *TestObject::New()); EXPECT_EQ(defaultConstructedImage, *ImageType::New()); } elastix-5.2.0/Common/GTesting/elxElastixMainGTest.cxx000066400000000000000000000031051474534065100225750ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ // First include the header file to be tested: #include "elxElastixMain.h" #include // Tests retrieving the component data base and a component creator in parallel. GTEST_TEST(ElastixMain, GetComponentDatabaseAndCreatorInParallel) { // TODO Make iterations like this run in parallel, for the sake of the test. for (auto i = 0; i <= 9; ++i) { const auto creator = elx::ElastixMain::GetComponentDatabase().GetCreator("Elastix", 1); EXPECT_NE(creator, nullptr); EXPECT_NE(creator, elx::ComponentDatabase::PtrToCreator{}); if (creator != nullptr) { const auto elxComponent = creator(); EXPECT_NE(elxComponent, nullptr); EXPECT_NE(elxComponent, itk::Object::Pointer{}); EXPECT_NE(dynamic_cast(elxComponent.GetPointer()), nullptr); } } } elastix-5.2.0/Common/GTesting/elxGTestUtilities.h000066400000000000000000000136711474534065100217700ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef elxGTestUtilities_h #define elxGTestUtilities_h #include #include #include #include #include // ITK header files: #include #include #include #include #include #include #include // GoogleTest header file: #include #include // For generate_n. #include #include // For DBL_MAX. #include #include namespace elastix { namespace GTestUtilities { /// Expect that all keys of both specified maps are unique. template void ExpectAllKeysUnique(const TMap & map1, const TMap & map2) { const auto endOfMap2 = map2.end(); for (const auto & keyValuePair : map1) { EXPECT_EQ(map2.find(keyValuePair.first), endOfMap2); } } /// Makes a map by merging its two arguments together. template TMap MakeMergedMap(TMap map1, const TMap & map2) { // Note: This for-loop should be equivalent to C++17 `map1.merge(TMap{map2});` for (const auto & keyValuePair : map2) { map1.insert(keyValuePair); } return map1; } /// Creates a default `ElastixTemplate` object. /// for unit testing purposes. template itk::SmartPointer CreateDefaultElastixObject() { using FixedImageType = typename TElastix::FixedImageType; using MovingImageType = typename TElastix::MovingImageType; const auto elastixObject = TElastix::New(); elastixObject->SetConfiguration(elx::Configuration::New()); const auto fixedImageContainer = elx::ElastixBase::DataObjectContainerType::New(); fixedImageContainer->push_back(FixedImageType::New()); elastixObject->SetFixedImageContainer(fixedImageContainer); const auto movingImageContainer = elx::ElastixBase::DataObjectContainerType::New(); movingImageContainer->push_back(MovingImageType::New()); elastixObject->SetMovingImageContainer(movingImageContainer); return elastixObject; } /// Returns an `OptimizerParameters` object, filled with pseudo random floating point numbers between the specified /// minimum and maximum value. inline itk::OptimizerParameters GeneratePseudoRandomParameters(const unsigned numberOfParameters, const double minValue, const double maxValue = 1.0) { assert(minValue < maxValue); assert((maxValue - minValue) <= DBL_MAX); itk::OptimizerParameters parameters(numberOfParameters); std::mt19937 randomNumberEngine; std::generate_n(parameters.begin(), numberOfParameters, [&randomNumberEngine, minValue, maxValue] { return std::uniform_real_distribution<>{ minValue, maxValue }(randomNumberEngine); }); return parameters; } /// Does set up and initialize the specified advanced metric. template void InitializeMetric( itk::AdvancedImageToImageMetric & metric, const TFixedImage & fixedImage, const TMovingImage & movingImage, itk::ImageSamplerBase & imageSampler, itk::AdvancedTransform & advancedTransform, itk::InterpolateImageFunction & interpolator, const typename TFixedImage::RegionType & fixedImageRegion) { // In elastix, this member function is just called by elx::MetricBase::SetAdvancedMetricImageSampler, at // https://github.com/SuperElastix/elastix/blob/5.1.0/Core/ComponentBaseClasses/elxMetricBase.hxx#L313 metric.SetImageSampler(&imageSampler); // Similar to the six member function calls in `MultiResolutionImageRegistrationMethod2::Initialize()` "Setup the // metric", at // https://github.com/SuperElastix/elastix/blob/5.1.0/Common/itkMultiResolutionImageRegistrationMethod2.hxx#L118-L124 metric.SetMovingImage(&movingImage); metric.SetFixedImage(&fixedImage); metric.SetTransform(&advancedTransform); metric.SetInterpolator(&interpolator); metric.SetFixedImageRegion(fixedImageRegion); metric.Initialize(); } /// Represents the value and derivative retrieved from a metric (cost function). struct ValueAndDerivative { double value; itk::Array derivative; static ValueAndDerivative FromCostFunction(const itk::SingleValuedCostFunction & costFunction, const itk::OptimizerParameters & optimizerParameters) { static constexpr auto quiet_NaN = std::numeric_limits::quiet_NaN(); ValueAndDerivative valueAndDerivative{ quiet_NaN, itk::Array(optimizerParameters.size(), quiet_NaN) }; costFunction.GetValueAndDerivative(optimizerParameters, valueAndDerivative.value, valueAndDerivative.derivative); return valueAndDerivative; } }; } // namespace GTestUtilities } // namespace elastix #endif elastix-5.2.0/Common/GTesting/elxResampleInterpolatorGTest.cxx000066400000000000000000000121121474534065100245300ustar00rootroot00000000000000/*========================================================================= * * Copyright UMC Utrecht and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ // First include the header files to be tested: #include "BSplineResampleInterpolator/elxBSplineResampleInterpolator.h" #include "BSplineResampleInterpolatorFloat/elxBSplineResampleInterpolatorFloat.h" #include "LinearResampleInterpolator/elxLinearResampleInterpolator.h" #include "NearestNeighborResampleInterpolator/elxNearestNeighborResampleInterpolator.h" #include "RayCastResampleInterpolator/elxRayCastResampleInterpolator.h" #include "RDBSplineResampleInterpolator/elxRDBSplineResampleInterpolator.h" // For ReducedDimensionBSplineResampleInterpolator. #include "elxElastixTemplate.h" #include "elxGTestUtilities.h" #include "../Core/Main/GTesting/elxCoreMainGTestUtilities.h" // ITK header file: #include // GoogleTest header file: #include // Standard C++ header file: #include using ParameterValuesType = itk::ParameterFileParser::ParameterValuesType; using ParameterMapType = itk::ParameterFileParser::ParameterMapType; // Using-declarations: using elx::CoreMainGTestUtilities::CheckNew; using elx::GTestUtilities::ExpectAllKeysUnique; using elx::GTestUtilities::MakeMergedMap; namespace { template using ElastixType = elx::ElastixTemplate, itk::Image>; // All tests specific to a dimension. template struct WithDimension { template